I have created Many To Many relationships on both levels (database and Models) for (User) and (Role) Models, now how can I assign many roles to the user id = 2 for instance: like below?
$user = User::find(2);
????
return view('index', compact(''));
By using attach and insert array for multiple roles
$roleId = [1, 2, 3];
$user->roles()->attach($roleId);
There's also sync method, the difference is sync remove all roles associated by user only if its not included in array of roleId
// User has role_id 1, 2, 3
$roleId = [1, 3];
$user->roles()->sync($roleId); // Role id 2 removed
Docs
$projects = Project::find(collect(request()->get('projects'))->pluck('id')); // collect(...)->pluck('id') is [2, 1]
$projects->pluck('id'); // [1, 2]
I want the result to be in the original order. How do I achieve this?
Try $projects->order_by("updated_at")->pluck("id"); or "created_at" if that's the column you need them ordered by.
Referencing MySQL order by field in Eloquent and MySQL - SELECT ... WHERE id IN (..) - correct order You can pretty much get the result and have it order using the following:
$projects_ids = request()->get('projects'); //assuming this is an array
$projects = Project::orderByRaw("FIELD(id, ".implode(',', projects_ids).")")
->find(projects_ids)
->pluck('id'));
#Jonas raised my awareness to a potential sql injection vulnerability, so I suggest an alternative:
$projects_ids = request()->get('projects');
$items = collect($projects_ids);
$fields = $items->map(function ($ids){
return '?';
})->implode(',');
$projects = Project::orderbyRaw("FIELD (id, ".$fields.")", $items->prepend('id'))
->find($projects_ids);
The explanation to the above is this:
Create a comma separated placeholder '?', for the number of items in the array to serve as named binding (including the column 'id').
I solve this by querying the data one by one instead mass query.
$ids = collect(request()->get('projects'))->pluck('id');
foreach($ids as $id){
$projects[] = Project::find($id);
}
$projects = collect($projects);
$projects->pluck('id');
I have to do this manually because laravel collection maps all the element sorted by using ids.
I am creating a system of newsfeed, and as you can easily guess, it is beyond my skills.
Please be kind to put me on the right track or provide something I can go on with.
I have several hundred events (model name is Event1, table 'events')
I also have a pivot table in which users can assign any event's importance (values 0,1,2,3)
The relevant columns of the pivot table user_attitudes (Model Userattitude) are
id, item_type, item_id, importance, attitude, creator_id
An example three record are:
456 - event - 678 - 2 - 4
457 - event - 690 - 3 - 15
458 - event - 690 - 1 - 4
459 - participant - 45 - 1 - 4
Plain English: Total aggregated importance of the event #690 is '4', while the event #678 is '2'.
Therefore in my ranking the event #690 should be listed as first.
Just to see the bigger pic: the user #4 also rated participant # 45 as importance = 1.
The table services many models - the above example include two - just to give a better image of what I have.
WHAT I NEED:
I wish to print a ranking of top 5 events (and later other models). I wish to be able to use two methods of calculating the total score:
by counting the actual value (0,1,2,3)
by counting any value above 0 as 1 point.
I want to generate views which filter events by this criteria:
at least one user set the importance to '0' (I need it to flag an event as untrustworthy)
events which has not been rated yet
reverse of the above - events which are rated by at least one user
events listed by number of users who assigned any importance to it
This is easy, but still I have no idea how to make it happen. The same filters as the above #2, but related to a particular user decisions:
list 5 or 10 events (random or newest) which has not yet been rated by the user
maybe something like this would be an answer:
$q->where('creator_id', '=', Auth::user()->id);
Relevant code:
As I don't really grasp the merged relations, I might fail to show everything needed to provide help - ask for more code in comments.
Models:
Event1 (table 'events'):
public function importances()
{
return $this->morphMany('Userattitude', 'item');
}
public function user_importance($user)
{
return $this->morphMany('Userattitude', 'item')->where('creator_id', ($user ? $user->id : NULL))->first();
}
User: (table 'users' - standard user table)
public function importances()
{
return $this->hasMany('Userattitude', 'creator_id');
}
In model Userattitude (different from User, table name 'user_attitudes')
public function events()
{
return $this->morphTo('item')->where('item_type', 'event');
}
public function event()
{
return $this->belongsTo ('Event1', 'item_id');
}
PROBLEMS IN REPLY TO #lucas answer:
PROBLEM 1.
table name 'items' keeps me confused as in my project 'items' are events (model Event1), the participants (model Entity) and other objects.
Can we stick to my naming until I get hold of the knowledge you are providing?
it also contains column named attitudes, which is used for blacklisting particular items.
For instance, an item of type 'entity' (possible participant of multiple events) can be voted by user two-wise:
- by importance set by an user (we are doing this now, values available to use are 0,1,2,3)
- by attitude of an user toward (possible value (-1, 0, 1)
Such solution allows me to compute karma of each item. For instance -1 x 3 = -3 (worst possible karma value), while 1 x 2 = 2 (medium positive karma).
In consequence I am unable to use queries with the users method. It is still too confusing to me, sorry. We diverted too far from my original mental image.
Consider this query:
$events = Event1::has('users', '<', 1)->get();
If in Event1 I declare
public function users()
{
return $this->morphToMany('User', 'item', null, null, 'creator_id');
}
Note: User is the standard users table, where username, password and email are stored
I get this error:
[2014-12-28 05:02:48] production.ERROR: FATAL DATABASE ERROR: 500 = SQLSTATE[42S02]: Base table or view not found: 1146 Table 'niepoz_niepozwalam.items' doesn't exist (SQL: select * from `Events` where (select count(*) from `users` inner join `items` on `users`.`id` = `items`.`creator_id` where `items`.`item_id` = `Events`.`id` and `items`.`item_type` = Event1) >= 1) [] []
if I change the method definition to
public function users()
{
return $this->morphToMany('Userattitude', 'item', null, null, 'creator_id');
}
Note: Userattitude is model (table name is 'user_attitudes') where i store user judgments. This table contains columns 'importance' and 'attitude'.
I get the same error.
If I change the method to
public function users()
{
return $this->morphToMany('User', 'Userattitudes', null, null, 'creator_id');
}
I get this:
[2014-12-28 05:08:28] production.ERROR: FATAL DATABASE ERROR: 500 = SQLSTATE[42S22]: Column not found: 1054 Unknown column 'user_attitudes.Userattitudes_id' in 'where clause' (SQL: select * from Events where (select count(*) from users inner join user_attitudes on users.id = user_attitudes.creator_id where user_attitudes.Userattitudes_id = Events.id and user_attitudes.Userattitudes_type = Event1) >= 1) [] []
Possible solution:
the 'user_attitudes' table alias with name 'items'.
I could create a view with the required name.
I did it, but now the query produces no results.
PROBLEM 2
should I rename creator_id into user_id - or keep both columns and keep duplicated information in them? The creator_id follows conventions and I use it to create records... how to resolve this dillema?
PROBLEM 3.
As far as I understand, if I want to get a USER-RELATED list of top-5 events,
I need to ad another line to the code, which narrows search scope to records created by a particular logged in user:
Auth::user()->id)
The code would look like this:
All with importance 0
$events = Event1::whereHas('users', function($q){
$q->where('importance', 0);
$q->where('creator_id', '=', Auth::user()->id);
})->get();
right?
PROBLEM 5:
Ok, I am now able to output a query like these:
$rank_entities = Entity::leftJoin('user_attitudes', function($q){
$q->on('entity_id', '=', 'entities.id');
$q->where('item_type', '=', 'entity');
})
->selectRaw('entities.*, SUM(user_attitudes.importance) AS importance')
->groupBy('entities.id')
->orderBy('importance', 'desc')
->take(6)
->get();
and in the foreach loop I can display the total importance count with this code:
{{$e->importance or '-'}}
But How I could display count of an alternative query: SUM of values from another column, named attitude, which can be computed in this SEPARATE query:
In other words, in my #foreach loop I need to display both $e->importance and a computed SUM(user_attitudes.attitude) AS karma, which for now can be received with this query:
$rank_entities = Entity::leftJoin('userattitudes', function($q){
$q->on('entity_id', '=', 'entities.id');
$q->where('item_type', '=', 'entity');
})
->selectRaw('entities.*, SUM(userattitudes.karma) AS karma')
->groupBy('entities.id')
->orderBy('karma', 'desc')
->take(5)
->get();
My solution would be to create some extra columns in the 'entities' table:
- karma_negative
- karma_positive
to store/update total amount of votes each time someone is voting.
First, let's talk about the setup. I wasn't entirely sure how and if your's works but I created this on my testing instance and it worked, so I recommend you change yours accordingly:
Database
events
That's a simple one (and you probably already have it like this
id (primary key)
name (or something like that)
etc
users
I'm not sure if in your example that is Userattitude but I don't think so...
id (primary key)
email (?)
etc
items
This is the important one. The pivot table. The name can be different but to keep it simple and follow conventions it should be the plural of the polymorphic relation (in your case item => items)
id (actually not even necessary, but I left it in there)
item_type
item_id
importance
creator_id (consider changing that to user_id. This would simplify the relationship declaration)
Models
I think you have to read the docs again. You had several weird relations declared. Here's how I did it:
Event1
By default Laravel uses the classname (get_class($object)) as value for the ..._type column in the database. To change that you need to define $morphClass in your models.
class Event1 extends Eloquent {
protected $table = 'events';
protected $morphClass = 'event';
public function users()
{
return $this->morphToMany('User', 'item', null, null, 'creator_id');
}
}
User
class User extends Eloquent implements UserInterface, RemindableInterface {
// ... default laravel stuff ...
public function events(){
return $this->morphedByMany('Event1', 'item', null, null, 'creator_id');
}
}
Queries
Alright now we can get started. First one additional information. I used Eloquent relations whenever possible. In all the queries a join() is made it would be slower to use relations because certain things (like counting or calculating the maximum) would have to be done in PHP after the query. And MySQL does a pretty good job (also performance wise) at those things.
Top 5 by total value
$events = Event1::leftJoin('items', function($q){
$q->on('item_id', '=', 'events.id');
$q->where('item_type', '=', 'event');
})
->selectRaw('events.*, SUM(items.importance) AS importance')
->groupBy('events.id')
->orderBy('importance', 'desc')
->take(5)
->get();
Top 5 by number of votes over 0
$events = Event1::leftJoin('items', function($q){
$q->on('item_id', '=', 'events.id');
$q->where('item_type', '=', 'event');
$q->where('importance', '>', 0);
})
->selectRaw('events.*, COUNT(items.id) AS importance')
->groupBy('events.id')
->orderBy('importance', 'desc')
->take(5)
->get();
All with importance 0
$events = Event1::whereHas('users', function($q){
$q->where('importance', 0);
})->get();
All without any votes
$events = Event1::has('users', '<', 1)->get();
All with 1+ votes
$events = Event1::has('users')->get();
All ordered by number of votes
$events = Event1::leftJoin('items', function($q){
$q->on('item_id', '=', 'events.id');
$q->where('item_type', '=', 'event');
})
->selectRaw('events.*, COUNT(items.id) AS count')
->groupBy('events.id')
->orderBy('count', 'desc')
->get();
Newest 5 without votes
If you are using Eloquents timestamps created_at:
$events = Event1::has('users', '<', 1)->latest()->take(5)->get();
If you're not (order by greatest id):
$events = Event1::has('users', '<', 1)->latest('id')->take(5)->get();
Random 5 without votes
$events = Event1::has('users', '<', 1)->orderByRaw('RAND()')->take(5)->get();
I did not add any explanations to the queries on purpose. If you want to know more about something specific or need help, please write a comment
PROBLEM 4: SOLVED! (credit to #lukasgeiter)
If you wish to display a ranking of items and limit the results to a particular tag defined in a pivot table, this is the solution:
$events = Event1 (table name = 'events')
For example, the tag would be war: defined in table
eventtags
Event nature are defined as
id = '1' is name = 'wars'
id = '2' is name = 'conflicts'
pivot table, which assigns multiple tags:
event_eventtags they are defined as id = '4'
Example records for event_eventtags:
id - event_id - eventtag_id
1 - 45 - 1
2 - 45 - 2
Plain English: the Event1 #45 is tagged as war(#1) and conflict(#2)
Now in order to print a list of 10 wars you should define your query in this way:
$events= Entity::join('event_eventtags', function($q){
$q->on('entity_id', '=', 'entities.id');
$q->where('entitycapacitytypes_id', '=', 1);
})->leftJoin('user_attitudes', function($q){
$q->on('item_id', '=', 'entities.id');
$q->where('item_type', '=', 'entity');
})
->selectRaw('entities.*, SUM(user_attitudes.importance) AS importance')
->groupBy('entities.id')
->orderBy('importance', 'desc')
->take(10)
->get();
The user_attitudes is part of voting system described in the original question. You can remove it and sort events by another method.
4 for on on my applications with Doctrine.
In there I'm using the following doctrine command to retrieve person object collection
//query
$people = $q->execute();
This return 20 objects. The primary key of the person object is a composite key with three attributes. Those are
id
department_id
name
I need to get person objects by searching in it as follows.
$id = 10;
$department_id = 1;
$name = "abc";
$people->get($id, $department_id, $name);
But this doesn't work and not give correct results. I tried with this and it gives null results which seems my collections primary key is not set.
$people->getKeyColumn();
I don't want to go through a foreach loop in collection and process it because when I deal with about 500 people, it slow down my application.
Can some one help me with this issue to get values from a doctrine collection.
Can you use something like this?
$people = Doctrine::getTable('Persons')
->createQuery()
->where('id = ? AND department_id = ? AND name = ?', array($id, $department_id, $name))
->execute();
It will get you a DoctrineCollection already filtered by the parameters provided.
'Persons' here is a Doctrine model name, not a table name from mySQL.
You can also use Doctrine's magic finders findBy*():
$people = Doctrine_Core::getTable('Persons')
->findByIdAndDepartmentIdAndName($id, $department_id, $name);
I have two tables, content and images (and a ContentImages table for the one to many relation, so that's actually 3 tables).
The following code saves the relation (in the action > updateContentFromRequest() ):
$ids = $this->getRequestParameter('contentImages');
if( isset($ids) ){
$ImagesTable = Doctrine::getTable('Content')->getRelation('Images')->getTable();
$associationName = Doctrine::getTable('Content')->getRelation('Images')->getAssociationTable()->getOption('name');
$this->content->$associationName->delete();
foreach ($ids as $id){
$id = explode('/', $id);
$this->content->get('Images')->add($ImagesTable->find($id));
}}
I changed the model to include a sort field in the ContentImages table:
content_id
image_id
sort (numeric)
The sort number is simply that, a number (0,1,2,3 etc)
$sort = $this->getRequestParameter('contentImagesSort');
How do I save the sort number? I do not want to add a sort field to the Image table because that could create difficulties when images are re-used across more content items. When a content item is new I do not know the ID yet so I'm a bit stumped...
If you have generated models you can add to your setUp method the orderBy parameter:
$this->hasMany('PICTURE as PICTURES', array(
'local' => 'BRAND_ID',
'foreign' => 'PICTURE_ID',
'refClass' => 'BRAND_PICTURE',
'orderBy' => 'your_field DESC'
));
orderBy should do the trick
You should add One to Many associations on your join Table, like:
ContentImages:
relations:
Image:
local: image_id
foreign: id
Content:
local: content_id
foreign: id
As such, you will be able to query directly the join table like this :
$q = Doctrine_Query::create()
->from('Content c')
->leftJoin('c.ContentImages ci')
->leftJoin('c.Images i')
->execute();
Then you can access to ContentImages using
$content->ContentImages->setSort('your sort');
This should do the trick :)
I do things a little a differently from what you've got above so not completely sure this is what you're asking, but can't you just save the object with whatever is needed for it?
$association = // load up existing entry from ContentImages using whatever method
$association->setSort($the_sort_I_want_to_add);
$association->save();
Or querying it...
$association = // load up existing entry from ContentImages using whatever method
$my_unknown_sort = $association->getSort();
Hope that helps.