How to Define and Use N:M Relationships in Laravel Eloquent

How to Define and Use N:M Relationships in Laravel Eloquent
Photo by Bryson Hammer / Unsplash

Many-to-many (N:M) relationships are a fundamental concept in relational databases. Laravel Eloquent makes it easy to define and work with these relationships using pivot tables. In this guide, we’ll walk through how to create an N:M relationship using generic tables for a Products and Categories example.

What Is an N:M Relationship?

An N:M relationship exists when multiple records in one table relate to multiple records in another. For instance:

  • A product can belong to multiple categories.
  • A category can include multiple products.

To implement this, a pivot table is used to store the relationships between the two entities.

Example Scenario: Products and Categories

We’ll create the following:

  1. products table: Contains product details.
  2. categories table: Contains category details.
  3. category_product (pivot table): Links products with categories.

Step 1: Creating the Database Tables

Migration for the Pivot Table

<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateCategoryProductTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('category_product', function (Blueprint $table) {
            $table->unsignedBigInteger('product_id');
            $table->unsignedBigInteger('category_id');

            // Composite primary key
            $table->primary(['product_id', 'category_id']);

            // Foreign key constraints
            $table->foreign('product_id')->references('id')->on('products')->onDelete('cascade');
            $table->foreign('category_id')->references('id')->on('categories')->onDelete('cascade');

            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('category_product');
    }
}

Key Elements:

  • Pivot Table Name: Named category_product by convention (alphabetical order of related table names).
  • Composite Primary Key: Ensures a unique combination of product_id and category_id.
  • Foreign Keys: Links the pivot table to products and categories.

Run the migration with:

php artisan migrate

Step 2: Define Eloquent Models and Relationships

Product Model (Product)

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Product extends Model
{
    protected $table = 'products';

    public function categories()
    {
        return $this->belongsToMany(Category::class, 'category_product', 'product_id', 'category_id');
    }
}

Category Model (Category)

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Category extends Model
{
    protected $table = 'categories';

    public function products()
    {
        return $this->belongsToMany(Product::class, 'category_product', 'category_id', 'product_id');
    }
}

Step 3: Using the Relationship in Laravel

Now that the relationships are defined, let’s perform CRUD operations.

Attach Categories to a Product

To link a product with one or more categories:

$product = Product::find($productId);
$product->categories()->attach($categoryIds);

Detach Categories from a Product

To remove a relationship between a product and categories:

$product->categories()->detach($categoryIds);

Sync Categories for a Product

To replace all existing categories for a product with a new set:

$product->categories()->sync($categoryIds);

Retrieve Categories for a Product

To get all categories a product belongs to:

$product->categories;

Retrieve Products for a Category

To get all products in a specific category:

$category->products;

Step 4: Best Practices

  1. Table Naming: Use descriptive names for the pivot table, combining the related tables in alphabetical order.
  2. Primary Keys: Use composite primary keys in the pivot table to ensure uniqueness.
  3. Cascade Deletes: Set onDelete('cascade') on foreign key constraints to maintain referential integrity when a product or category is deleted.

Bonus: Querying Relationships with Constraints

You can add constraints when querying relationships. For example:

Products in a Specific Category

$products = Category::where('name', 'Electronics')->first()->products;

Categories of a Product Created in the Last 30 Days

$categories = Product::where('created_at', '>=', now()->subDays(30))->first()->categories;

Conclusion

Defining and using N:M relationships in Laravel Eloquent is a straightforward process. By leveraging pivot tables and Eloquent’s belongsToMany methods, you can effectively manage complex relationships in your application. This approach is flexible, scalable, and maintains the relational integrity of your data.

Start implementing these techniques to simplify and organize your many-to-many relationships today!

Subscribe to codingwithalex

Don’t miss out on the latest issues. Sign up now to get access to the library of members-only issues.
[email protected]
Subscribe