0
votes

Is it possible to make Laravel relation through belongsToMany relations?

I have 4 tables:

1)Restaurants (id , name) - uses hasManyRelation with Workers table

2)Directors (id , name)

3)Directors_Restaurants (id, director_id, restaurant_id) - pivot table for connecting belongsToMany Restaurants with Directors

3)Workers (id, name, restaurant_id)

With this function in Directors model i can get all connected restaurants

public function restaurants()
{
    return $this->belongsToMany('App\Restaurant','director_restaurant');
}

With this function in my code i can get all workers of all restaurants of one director

$director = Director::find(1);
$director->load('restaurants.workers');
$workers = $director->restaurants->pluck('workers')->collapse();

So my question is : can i declare similar relation in my Director model to get all its workers of all its restaurants?

4

4 Answers

2
votes

Of course you can have hasMany relationship method on Director model with Eager Loading

just like below

public function restaurants()
{
    return $this->hasMany(Restaurant::class)->with('restaurants.workers');
}
1
votes

i can suggest a solution like this:

Director Model OPTION 1

public function getAllRestaurants(){
    return $this->hasMany(Restaurant::class)->with('restaurants.workers');
}

Director Model OPTION 2

public function getAllRestaurants(){
    $this->load('restaurants.workers');
    return $this->restaurants->pluck('workers')->collapse();
}

You can get all restaurants anywhere

$all_restaurants = Director::find(1)->getAllRestaurants();
1
votes

You can define a direct relationship by "skipping" the restaurants table:

class Director extends Model
{
    public function workers()
    {
        return $this->belongsToMany(
            Worker::class,
            'director_restaurant',
            'director_id', 'restaurant_id', null, 'restaurant_id'
        );
    }
}
1
votes

You can define an accessor method in your model to hide some of the logic

# App/Director.php

// You'll need this line if you want this attribute to appear when you call toArray() or toJson()
// If not, you can comment it
protected $appends = ['workers'];

public function getWorkersAttribute()
{
    return $this->restaurants->pluck('workers')->collapse();
}

# Somewhere else
$director = Director::with('restaurants.workers')->find(1);
$workers = $director->workers;

But ultimately, you still have to load the nested relationship 'restaurants.workers' for it to work.

Given your table attributes you could also define a custom HasMany relationship that looks like this

# App/DirectorRestaurant.php

public function workers()
{
    return $this->hasMany(Worker::class, 'restaurant_id', 'restaurant_id');
}

# Somewhere else
$director = Director::find(1);
$workers = DirectorRestaurant::where('director_id', $director->id)->get()->each(function($q) { $q->load('workers'); });

But I don't recommend it because it's not very readable.

Lastly, there's the staudenmeir/eloquent-has-many-deep package where you can define that sort of nested relationship.

https://github.com/staudenmeir/eloquent-has-many-deep