Actually you are doing this the right way. There is no has many through relationship with many to many.
The only workaround is to have a board_user table.
Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.
What is the simplest way to achieve this relationship query in Eloquent?
I have many boards, which each may have many roles, which each may have many assignee staff members.
I could normally compile the array this way.
$staff = [];
foreach ($board->roles as $role)
{
foreach ($role->users as $user)
{
$staff[] = $user;
}
}
return $staff;
I can't just do a "has many through" because there's actually 4 tables here. Between Role and User there is a UserRole intermediary table that handles a many-to-many relationship between a User and his Roles.
Actually you are doing this the right way. There is no has many through relationship with many to many.
The only workaround is to have a board_user table.
@Jawsh You can actually do that using Eloquent only and it will work for any deep nested relation:
$board->load(['roles.users' => function($query) use (&$staff) {
$staff = $query->get()->unique();
}]);
dd($staff);
The reason we are using &$staff here (by reference) is so we can use this variable outside the anonymous function.
You can use this with the with() method as-well and do that for any deep nested relationship.
Disclaimer: I did not "invent" that trick. I read it few months ago somewhere. I can't recall where, unfortunately, if someone knows where I read that, please provide the link :)
@pmall Well there is another option but having a board_user table, take a look at my answer above. Also, if he wants to use his solution, he has to make sure he's eager loading the relations and removing duplicates. As his code stands now, he's gonna query the DB TONS of times and he will have duplicates.
He can maybe do something like:
$staff = [];
$board->load('roles.users');
foreach ($board->roles as $role)
{
foreach ($role->users as $user)
{
$staff[] = $user;
}
}
$staff = array_unique($staff, SORT_REGULAR);
return $staff;
I would still suggest my answer above.
@Jawsh @kfirba Here's the origin and a bit more info http://softonsofa.com/laravel-querying-any-level-far-relations-with-simple-trick/
ps. In this case, you don't even need the eager loading trick. For 2 levels it's enough to use Collection methods (check the comments from Joseph Silber below the article):
// roles.users must be eager loaded
$board->roles->lists('users')->collapse()->unique();
@JarekTkaczyk oh I love the lists trick thanks
@JarekTkaczyk Thanks for the link :) Now I have the resource.
How does the list solution work? Are you listing a relationship? What is the result of listing a relationship? Never knew it was possible. I thought it was only possible for a field.
@kfirba It works like with any other property/attribute (just mind that the relation must be eager loaded):
// given role has name attribute, you can access these just the same
$role->name // single value
$role->users // collection
// then
$board->roles->lists('name')
// collection {
// 'name_1',
// 'name_2',
// ...
// }
$board->roles->lists('users')
// collection {
// collection_1 { ... }
// collection_2 { ... }
// ...
// }
So:
$board->roles->lists('users') // collection of collections
->collapse() // flattened collection of users
->unique()
@JarekTkaczyk Oh I see! I've actually never thought about that this way. Thanks a lot!
@JarekTkaczyk there is one thing I want to ask, why won't we simply do something like:
$board->roles->users
Instead of
$board->roles->lists('users')
Won't they both return a collection with the users?
@kfirba Obviously there's no users available on the roles collection, but rather on each of its elements.
@JarekTkaczyk Oh right :) Thanks!
I extended the Eloquent collection and added this method:
class CustomCollection extends Collection
{
public function __get($relationOrProperty) {
if(method_exists($this->first(), $relationOrProperty))
{
$this->load($relationOrProperty);
return $this->pluck($relationOrProperty)->collapse()->unique();
}
elseif($this->first()->$relationOrProperty)
{
return $this->pluck($relationOrProperty)->unique();
}
throw new Exception('Method or property "' . $relationOrProperty . '" does not exist.');
}
}
Now I can use $board->roles->users
Does this make sense? Any dangers because of using the magic method? Maybe something similar is already built in in the meantime?
If there is a problem with it, we could still use a separate method like $board->roles->collect('users')
Hi @SebastianSchöps ,
Any new insights on the polymorphic hasManyThrough?
I am implementing this relationship right now. But whatever method I try, it just doesn't feel right.
I created a HasManyThrough relationship with support for BelongsToMany: Repository on GitHub
After the installation, you can use it like this:
class Board extends Model {
use \Staudenmeir\EloquentHasManyDeep\HasRelationships;
public function staff() {
return $this->hasManyDeep(User::class, [Role::class, 'role_user']);
}
}
Thanks @ staudenmeir
It works!!!!
It is a Laravel 5.5 composer package that can perform multi-level relationships (deep)
Package: https://github.com/staudenmeir/eloquent-has-many-deep
Example:
User --> belongs to many --> Role --> belongs to many --> Permission
class User extends Model
{
use \Staudenmeir\EloquentHasManyDeep\HasRelationships;
public function permissions()
{
return $this->hasManyDeep(
'App\Permission',
['role_user', 'App\Role', 'permission_role'], // Pivot tables/models starting from the Parent, which is the User
);
}
}
Example if foreign keys need to be defined:
https://github.com/staudenmeir/eloquent-has-many-deep/issues/7#issuecomment-431477943
Please or to participate in this conversation.