简体   繁体   中英

Many to Many to Many relation in laravel

Here my tables migrations(4):

restaurants:

Schema::create('restaurants', function (Blueprint $table) {
        $table->increments('id');
        $table->string('name');
});

foods:

Schema::create('foods', function (Blueprint $table) {
        $table->increments('id');
        $table->string('name');
});

ingredients:

Schema::create('ingredients', function (Blueprint $table) {
        $table->increments('id');
        $table->string('name');
});

restaurant_has_foods_with_ingredients:

Schema::create('restaurant_has_foods_with_ingredients', function (Blueprint $table) {
        $table->increments('id');
        $table->unsignedInteger('restaurant_id');
        $table->unsignedInteger('food_id');
        $table->unsignedInteger('ingredient_id');

        $table->foreign('restaurant_id')
            ->references('id')
            ->on('restaurants')
            ->onDelete('cascade');

        $table->foreign('food_id')
            ->references('id')
            ->on('foods')
            ->onDelete('cascade');

        $table->foreign('ingredient_id')
            ->references('id')
            ->on('ingredients')
            ->onDelete('cascade');
});

How can I define my Restaurant,Food,Ingredient Models with their relations?

Here some examples of my needs:

1-All restaurants with specific ingredient in their serving dishes.

2-All ingredients of a specific dish in a specific restaurant.

3-All dishes with a specific ingredient in a restaurant.

...

-------------------------After Edit-----------------------------

I have my own solution but I think it's not a good one.

Now in my Restaurant model I have two implementation of getting food

One to get all foods of a restaurant:

public function foods()
{
    return $this->belongsToMany('App\Models\Food', 'restaurant_has_foods_with_ingredients')
        ->groupBy('food_id');
}

And another one to get ingredients of current restaurunt's specific food

public function foodIngredients(Food $food)
{
    $result = DB::table('restaurant_has_foods_with_ingredients')
        ->select('restaurant_has_foods_with_ingredients.ingredient_id as ingredient_id')
        ->where('restaurant_has_foods_with_ingredients.restaurant_id',$this->id)
        ->where('restaurant_has_foods_with_ingredients.food_id',$food->id)
        ->get();

    $ingredients = array();

    foreach ($result as $row) {
        $ingredients[] = Ingredient::find($row->ingredient_id);
    }
    return $ingredients;
}

Basicly its something like this :

Create two migration : restaurant_food and food_ingredient

we have a

Restaurant model - Food model - ingredient model

A Restaurent can have many types of food and a food can be in served restaurent -> so we have a many to many relation here

Restaurant model

class Restaurant extends Model
{
    /**
     * The foods that belong to the Restaurant.
     */
    public function foods()
    {
        return $this->belongsToMany('App\Food');
    }

}

Alright now with the next thing

1- As we mentioned before , a food type can be served in many restaurants so we need to define the inverse relation.

2- A food has many ingredients and an ingredient can be used in many types of food -> Another many to many

Food model

class Food extends Model
{
     /**
     * The ingredients that belong to the Food.
     */
    public function restaurants()
    {
        return $this->belongsToMany('App\Restaurant');
    }
    /**
     * The ingredients that belong to the Food.
     */
    public function ingredients()
    {
        return $this->belongsToMany('App\Ingredient');
    }

}

Now the same goes for

Ingredient model

class Ingredient extends Model
{
    /**
     * The foods that belong to the Ingredient.
     */
    public function foods()
    {
        return $this->belongsToMany('App\Food');
    }

}

Alright now we have everything setup this is how it's used

Adding to a relation

$Restaurant = Restaurant::find($id);
$Restaurant->foods()->attach($food_id);

Removing from a relation

$Restaurant->foods()->detach($food_id);

1-All restaurants with specific ingredient in their serving dishes.

$restaurants = App\Restaurant::with(['foods' => function ($query) {
    $query->whereHas(['ingredients' => function ($query) {
      $query->where('name', 'like', 'potato');

}])->get();

2-All ingredients of a specific dish in a specific restaurant.

$ingridients = App\Ingredient::whereHas(['foods' => function ($query) {
    $query->where('name', 'like', 'potato')->whereHas(['restaurant' => function ($query) {
      $query->where('name', 'like', 'newyork');

}])->get();

3-All dishes with a specific ingredient in a restaurant.

$foods= App\Food::whereHas(['ingredients' => function ($query) {
    $query->where('name', 'like', 'potato');
},
'restaurants' => function ($query) {
        $query->where('name', 'like', 'newyork');
   }
])->get();

change potato/newyork with a variable and you are good to go

My code might have some minor typos or mistakes but i hope you got the idea of how things work

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM