Relations BelongsToMany

24 mai 2025
22:27
intermédiaire

Relations BelongsToMany : Créer des relations complexes

Bienvenue dans ce huitième épisode de notre série sur Laravel 12 ! 🚀 Aujourd'hui, nous allons explorer les relations BelongsToMany (Many-to-Many), une relation essentielle pour créer des liens complexes entre vos modèles.

Introduction

Dans notre épisode précédent, nous avons vu les relations HasMany/BelongsTo. Aujourd'hui, nous allons transformer notre système d'ingrédients pour utiliser une relation Many-to-Many, ce qui nous permettra de mieux structurer nos données et d'effectuer des requêtes plus complexes.

📌 Au programme :

  • ✅ Comprendre le concept de relation Many-to-Many
  • ✅ Créer une table pivot
  • ✅ Implémenter la relation BelongsToMany
  • ✅ Attacher et détacher des relations
  • ✅ Optimiser nos requêtes

Pourquoi une relation Many-to-Many ?

Prenons l'exemple de nos recettes et ingrédients :

  • Une recette peut avoir plusieurs ingrédients
  • Un ingrédient peut être utilisé dans plusieurs recettes

C'est exactement ce qu'on appelle une relation Many-to-Many. Contrairement à une relation HasMany où un modèle "possède" plusieurs autres modèles, ici les deux modèles peuvent avoir plusieurs relations entre eux.

Créer notre modèle Ingredient

Commençons par créer notre modèle Ingredient :

php artisan make:model Ingredient -m

Dans la migration, nous allons définir une structure simple :

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

Créer la table pivot

Pour une relation Many-to-Many, nous avons besoin d'une table pivot. La convention de nommage de Laravel est simple : prenez les noms des deux modèles au singulier dans l'ordre alphabétique.

php artisan make:migration create_ingredient_recipe_table

Dans cette migration :

public function up()
{
    Schema::create('ingredient_recipe', function (Blueprint $table) {
        $table->id();
        $table->foreignId('ingredient_id')->constrained()->cascadeOnDelete();
        $table->foreignId('recipe_id')->constrained()->cascadeOnDelete();
        $table->timestamps();
    });
}

Définir les relations dans nos modèles

Dans le modèle Recipe

use Illuminate\Database\Eloquent\Relations\BelongsToMany;

public function ingredients(): BelongsToMany
{
    return $this->belongsToMany(Ingredient::class);
}

Dans le modèle Ingredient

use Illuminate\Database\Eloquent\Relations\BelongsToMany;

public function recipes(): BelongsToMany
{
    return $this->belongsToMany(Recipe::class);
}

Attacher des relations

Pour attacher des ingrédients à une recette, nous pouvons utiliser la méthode attach() :

$recipe = Recipe::find(1);
$recipe->ingredients()->attach($ingredientId);

Pour notre cas d'usage, nous pouvons créer une route temporaire pour attacher les ingrédients :

Route::get('/attach', function () {
    $ingredients = [
        ['name' => 'Spaghetti'],
        ['name' => 'Eggs'],
        ['name' => 'Cheese'],
        ['name' => 'Bacon'],
        // ... autres ingrédients
    ];

    foreach ($ingredients as $ingredient) {
        $recipe = Recipe::find(1);
        $ingredient = Ingredient::find($ingredient['id']);
        $recipe->ingredients()->attach($ingredient->id);
    }
});

Utiliser les relations dans nos vues

Maintenant que nos relations sont en place, nous pouvons les utiliser dans nos vues :

@foreach($recipe->ingredients as $ingredient)
    <li>{{ $ingredient->name }}</li>
@endforeach

Conclusion

Les relations BelongsToMany sont puissantes et permettent de :

  1. Créer des relations complexes entre modèles
  2. Éviter la duplication de données
  3. Effectuer des requêtes efficaces sur les relations

Dans notre prochain épisode, nous verrons comment optimiser ces relations et éviter les problèmes N+1.

Note : N'oubliez pas que la table pivot peut être enrichie avec des colonnes supplémentaires si nécessaire, comme par exemple la quantité d'un ingrédient dans une recette.