Search

Laravel Eloquent Many to Many Relationship Tutorial with Example

post-title

While designing database structure, you may get to know that some tables are related to one another. For example in a blog application, a one country have many states and states have many cities. In a MySQL database tables, we add one table's id to another table or create pivot table to create relationship between tables.

Laravel provides eloquent relationship which provides powerful query builders. In Laravel, eloquent relationships are defined in model classes. Laravel eloquent provides easy ways to create common relationships:

  • one to one
  • one to many
  • many to many
  • has one of many
  • has one through
  • has many through
  • one to one polymorphic
  • one to many polymorphic
  • many to many polymorphic

In this article, we will learn how to create many to many eloquent relationship between two models. This is slightly complicated than one to one and one to many relationship. For example, One Role may have more than one Permission and same way, one Permission can be assigned to more than one Role.

Example:

In this example, we assume that you have created fresh Laravel application. We also assume that you have confiured database connection.

Database tables

To create many to many relationship, we need three database tables: roles, permissions and permission_role. The permission_role table is created from the alphabetical order of the related model names and contains two fields: role_id and permission_id. The table is used as intermediate table between roles and permissions.

Migration

we need to create three migration table. Run the following three commands into Terminal to create migration classes at database/migrations directory.

php artisan make:migration create_roles_table
php artisan make:migration create_permissions_table
php artisan make:migration create_permission_role_table

Below are the migration table fields for these table:

roles migration

/**
 * Run the migrations.
 *
 * @return void
 */
public function up()
{
    Schema::create('roles', function (Blueprint $table) {
        $table->id();
        $table->string('name');
        $table->timestamps();
    });
}

permissions migration

/**
 * Run the migrations.
 *
 * @return void
 */
public function up()
{
    Schema::create('permissions', function (Blueprint $table) {
        $table->id();
        $table->string('name');
        $table->timestamps();
    });
}

permission_role migration

/**
 * Run the migrations.
 *
 * @return void
 */
public function up()
{
    Schema::create('permission_role', function (Blueprint $table) {
        $table->integer('role_id');
        $table->integer('permission_id');
        $table->timestamps();
    });
}

Run the migrate command to create tables into database.

php artisan migrate

Model

Laravel model located at app/Models directory. Create model classes for these tables using following Artisan commands one by one into Terminal.

php artisan make:model Role
php artisan make:model Permission

Now, you need to create permissions method in the Role model that will return belongsToMany method of Illuminate\Database\Eloquent\Model class.

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Role extends Model
{
    use HasFactory;

    /**
     * The roles that belong to the user.
     */
    public function permissions()
    {
        return $this->belongsToMany(Permission::class);
    }
}

The eloquent will automatically checks role_id and permission_id fields in the permission_role pivot table which relates to id field of both tables. You can also define pivot table into relationship method as second parameter.

return $this->belongsToMany(Permission::class, 'permission_roles');

To define reverse relationship, i.e., if you already have permission and you want to find all roles of the permission by relationship, you need to create roles method into Permission model.

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Permission extends Model
{
    use HasFactory;

    /**
     * The roles that belong to the permission.
     */
    public function roles()
    {
        return $this->belongsToMany(Role::class);
    }
}

Now, if your pivot table also have some extra fields which are required when you retrieve relationship data, then you need to specify them when defining the relationship:

return $this->belongsToMany(Role::class)->withPivot('is_active', 'created_at');

Route

In routes/web.php file, we have added new route for relationship testing.

<?php

use Illuminate\Support\Facades\Route;
use App\Http\Controllers\PermissionController;

Route::get('/permission', [PermissionController::class, 'index']);

Controller

As we have added route, also create PermissionController with following command.

php artisan make:controller PermissionController

Find all permissions for the specific role

Open the controller at app/Http/Controllers/PermissionController and create index method.

/**
 * Display a listing of the resource.
 *
 * @return \Illuminate\Http\Response
 */
public function index()
{
    $permissions = Role::find(1)->permissions;

    dd($permissions);
}

This will return all permissions of the the role with the id of 1. As all the relationship serve as query builder, you may also constraints in permissions() method.

/**
 * Display a listing of the resource.
 *
 * @return \Illuminate\Http\Response
 */
public function index()
{
    $permissions = User::find(1)->permissions()
        ->orderBy('created_at' 'desc')
        ->get();

    dd($permissions);
}

Find all roles for the selected permission

Now suppose you want to find all the roles which are assigned the permission, i.e., reverse relationship, here is how to access user record.

/**
 * Display a listing of the resource.
 *
 * @return \Illuminate\Http\Response
 */
public function index()
{
    $roles = Permission::find(1)->roles;

    dd($roles);
}

You can even eager load the relationship data while quaring parent model data using with() methdod.

/**
 * Display a listing of the resource.
 *
 * @return \Illuminate\Http\Response
 */
public function index()
{
    $roles = Role::with('permissions')->get();

    foreach($roles as $role) {
        echo($role->permissions);
    }
} 

I hope this will help you to understand many to many eloquent relationship.