Eloquent Many-To-Many-To-Many - How to Load Distant Relation Easily

Eloquent many-to-many-to-many - how to load distant relation easily

This is how you can do it:

User::where('id', $id)->with(['groups.permissions' => function ($q) use (&$permissions) {
$permissions = $q->get()->unique();
}])->first();

// then
$permissions; // collection of unique permissions of the user with id = $id

Retrieve distant relation through has-many-through for many-to-many relation in Laravel

User Model

public function groups()
{
return $this->belongsToMany('App\Group');
}

Group Model

public function tasks()
{
return $this->belongsToMany('App\Task');
}

Task Model

public function groups()
{
return $this->belongsToMany('App\Group');
}

Retrieving all tasks for a user.

$user = User::find(1);

$user->load('groups.tasks');

$tasks = $user->groups->pluck('tasks')->collapse();

Querying Distant Relationships

Case 1

In the case you have a relationship as shown in the diagram below

user accounts hotels relationship

What you are looking for is the hasManyThrough relationship.

From the Laravel documentation

The "has-many-through" relationship provides a convenient shortcut for accessing distant relations via an intermediate relation

In your case, on your User model, you can define the relationship

public function hotels()
{
return $this->hasManyThrough('App\Hotel', 'App\Account');
}

To then get your collection of hotels you can simply use

$hotels = Auth::user()->hotels;

You can also provide extra arguments to the hasManyThrough function to define the keys that are used on each table, great examples of this are given in the documentation linked above!

Case 2

If, instead, you have a relation as shown in the following diagram

distant many-to-many relation

It is a little more tricky (or, at least, less clean). The best solution I can think of, that uses the fewest queries is to use with.

$accounts = Auth::user()->accounts()->with('hotels')->get();

Will give you a collection of accounts, each with a hotels child. Now, all we have to do is get the hotels as a standalone collection, this is simple with some neat collection functions provided by Laravel.

$hotels = $accounts->flatMap(function($account) {
return $account->hotels;
})->unique(function ($hotel) {
return $hotel->id;
});

This will do the job of creating a collection of hotels. In my opinion, it would be cleaner and more efficient to simply make a new relationship as shown below.

a simpler many-to-many relationship

And then to perform queries, using basic Eloquent methods.

How to fetch count from distant relationship in laravel way?

I managed to get all my results in a single request with the below query. Thanks! @athul for showing the direction.

Attribute::where('filterable', '=', 1)->whereHas('products.categories', function (Builder $query) use($category) {
$query->where('categories.id', '=', $category->id);
})
->withCount(['products' => function ($query) use($category) {
$query->whereHas('categories', function (Builder $query) use ($category) {
$query->where('categories.id', '=', $category->id);
});
}])->get();

Laravel 5.1 Eloquent distant relationship with many to many models

You can use the hasManyThrough relationship to query for models through an intermediary model.

class Environment extends Model
{
public function services()
{
return $this->belongsToMany('App\Service');
}

public function serviceRoles()
{
return $this->hasManyThrough('App\ServiceRoles', 'App\Service');
}
}

You should be able to query the an environment model for all of it's service roles.

$environment = Environment::with('serviceRoles')->first();

// Should output a collection of ServiceRole models
dd($environment->serviceRoles);

using 'And Where' in Many to Many relation

Your code provided doesn't make a lot of sense, but based on your explanation, you want to find a user who has a relationship with cars 1 and 3 and 6. Using whereIn() gets you users with relationships with cars 1 or 3 or 6.

Your attempt with multiple where() filters wouldn't work, as this would be looking for a single row in the pivot table with multiple cars, which obviously wouldn't be possible. Instead, you need to nest multiple whereHas() relationship filters into a single where() group like this:

$users = User::where(function ($q) use ($car_selected) {
foreach ($car_selected as $car) {
$q->whereHas('cars', function ($query) use ($car) {
$query->where('car_id', $car);
});
}
})
->with(['cars' => function ($q) use ($car_selected) {
$q->whereIn('car_id', $car_selected);
}])
->get();

This is all assuming you've correctly set up your relationships and tables per Laravel standards.

Demo code is here: https://implode.io/anjLGG

Accessing distant relationship with belongstoMany

Use the whereHas() method:

Job::whereHas('page.users', function($q) {
$q->where('id', auth()->id());
})->get();

I assume that these relations are defined. If not, define those. In the Pages model:

public function users()
{
return $this->belongsToMany('App\Modules\Jobs\Entities\Users');
}

In the Jobs model:

public function page()
{
return $this->belongsTo('App\Modules\Jobs\Entities\Pages');
}

If you do not follow naming conventions, you also need to add pivot table name and foreign key names to these relationship definitions.



Related Topics



Leave a reply



Submit