问题
In a Laravel 5.2 application I have three models: User
, Role
and Task
.
A User
is associated with multiple Roles
, and a Role
is associated with multiple Tasks
.
Therefore each user is associated to multiple tasks, through their roles.
I am trying to access all Tasks
associated with a User
, through their Roles.
The relevant parts of my models look like:
class User extends Authenticatable
{
public function roles()
{
return $this->belongsToMany('App\Role');
}
public function tasks()
{
return $this->hasManyThrough('App\Task', 'App\Role');
}
}
class Role extends Model
{
public function tasks()
{
return $this->belongsToMany('App\Task');
}
public function users()
{
return $this->belongsToMany('App\User');
}
}
class Task extends Model
{
public function roles()
{
return $this->belongsToMany('App\Role');
}
}
The following returns an SQL error;
Column not found: 1054 Unknown column 'roles.user_id'
It seems to be trying to access the relationship through a (non-existent) foreign key in the Role model, rather than through the pivot table.
$user = Auth::user;
$tasks = $user->tasks;
How can I access all tasks related to a user through these relationships?
回答1:
I have developed a custom BelongsToManyThrough relationship which might interest you. You would need to add the new relation class (as given in my gist; it is too long to paste here), and also override your base Model
class as described in the gist to implement belongsToManyThrough
.
Then (assuming you are using Laravel's default table naming schemes - if not, you can specify the joining tables as well), you would define your relationship as:
public function tasks()
{
return $this->belongsToManyThrough(
'App\Task',
'App\Role');
}
belongsToManyThrough
will not only give you a list of Tasks for your User(s), it will also tell you the Role(s) via which each User has each Task. For example, if you had:
$user->tasks()->get()
The output would look something like:
[
{
"id": 2,
"name": "Ban spammers",
"roles_via": [
{
"id": 2,
"slug": "site-admin",
"name": "Site Administrator",
"description": "This role is meant for \"site administrators\", who can basically do anything except create, edit, or delete other administrators."
},
{
"id": 3,
"slug": "group-admin",
"name": "Group Administrator",
"description": "This role is meant for \"group administrators\", who can basically do anything with users in their same group, except other administrators of that group."
}
]
},
{
"id": 13,
"name": "Approve posts",
"roles_via": [
{
"id": 3,
"slug": "group-admin",
"name": "Group Administrator",
"description": "This role is meant for \"group administrators\", who can basically do anything with users in their same group, except other administrators of that group."
}
]
},
{
"id": 16,
"name": "Reboot server",
"roles_via": [
{
"id": 2,
"slug": "site-admin",
"name": "Site Administrator",
"description": "This role is meant for \"site administrators\", who can basically do anything except create, edit, or delete other administrators."
}
]
}
]
My custom relationship does this efficiently, with only a few queries, as opposed to other solutions involving foreach
, which would create an n+1 query problem.
回答2:
From the source code you shared it seems that you have Many to Many
relationships between: User
and Role
and between Role
and Task
.
The hasManyThrough
method expects two One to Many
relationships.
A possible way of getting all User
related Task
s would be: (inside the User
class)
public function getTasksAttribute()
{
$tasks = [];
foreach ($this->roles as $role) {
$tasks = array_merge($tasks, $role->tasks);
}
return $tasks;
}
You would then be able to access the tasks with:
$user->tasks;
回答3:
Instead of using array_merge()
One way to do this would be to add a getTasksAttribute()
method, and using collections.
public function getTasksAttribute()
{
$tasks = new Collection([]);
foreach ($this->roles as $role) {
$tasks = $tasks->mergeRecursive($role->tasks);
}
return $tasks;
}
This can be accessed with $user->tasks
来源:https://stackoverflow.com/questions/35306242/laravel-5-eloquent-hasmanythrough-belongstomanythrough-relationships