Many-to-Many relationship in Laravel - All you need to know

Many-to-Many relationship in Laravel - All you need to know

In this article, you will learn about Laravel's many-to-many relationship, starting from the basics and gradually progressing to more advanced concepts. We'll begin with a simple example involving users and projects, where we will establish a relationship between users and projects using a pivot table. After setting up the relationship, we will demonstrate how to store and fetch records from the database efficiently. By the end, you will have a deep understanding of how to implement many-to-many relationships in Laravel with practical use cases.

Setting Up the Database

Our tables for projects, users and a pivot table name project_user.
// users table Schema::create('users', function (Blueprint $table) { $table->id(); $table->string('name'); $table->string('email')->unique(); $table->timestamp('email_verified_at')->nullable(); $table->string('password'); $table->rememberToken(); $table->timestamps(); }); // projects table Schema::create('projects', function (Blueprint $table) { $table->id(); $table->string('name'); $table->timestamps(); }); // our pivot 'project_user' table Schema::create('project_user', function (Blueprint $table) { $table->foreignId('project_id')->constrained(); $table->foreignId('user_id')->constrained(); });

Relationship Setup

here we will define many-to-many relationship using the **belongsToMany() method**.
class Project extends Model { use HasFactory; protected $fillable = ['name']; public function users(): BelongsToMany { return $this->belongsToMany(User::class); } }

Creating Sample Data

We will generate sample data by using factories and seeder.
// ProjectFactory public function definition(): array { return [ 'name' => $this->faker->name(), ]; } // ProjectSeeder $projects = \App\Models\Project::factory(10)->create(); foreach ($projects as $project) { $users = User::inRandomOrder()->take(rand(1, 3))->pluck('id'); foreach ($users as $user) { $project->users()->attach($user); } }
Now, migrate and seed the database:
php artisan migrate:fresh --seed

Utilizing the Relationship

Now we will use our relationship to fetch the projects with users. users is our relationship which is present in Project model.
public function index() { $projects = Project::with('users')->get(); $data = ['projects' => $projects]; return view('welcome', $data); }
Now lets goto **welcome.blade.php** and paste this code.
<!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Laravel Many-to-Many Relationship</title> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous"> </head> <body> <div class="container mx-auto"> <div class="container d-flex align-items-center justify-content-center"> <table class="table table-striped w-75"> <thead> <tr> <th scope="col">#</th> <th scope="col">Projects</th> <th scope="col">Users</th> </tr> </thead> <tbody> @foreach ($projects as $project) <tr> <th>{{ $loop->index + 1 }}</th> <th>{{ $project->name }}</th> <td> <ul> @foreach ($project->users as $user) <li>{{ $user->name }}</li> @endforeach </ul> </td> </tr> @endforeach </tbody> </table> </div> </div> <script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.8/dist/umd/popper.min.js" integrity="sha384-I7E8VVD/ismYTF4hNIPjVp/Zjvgyol6VFvRkX/vR+Vc4jQkC+hVqc2pM8ODewa9r" crossorigin="anonymous"> </script> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.min.js" integrity="sha384-BBtl+eGJRgqQAUMxJ7pMwbEyER4l1g+O15P+16Ep7Q9Q+zqX6gSbd85u4mG4QzX+" crossorigin="anonymous"> </script> </body> </html>
![Example Image of normal many-to-many relationship!](http://127.0.0.1:8000/storage/uploads/images/PENrKr8iuL6iaXqQBI7WKf1hodbYkirZy93L3XTp.png)

1 - Adding timestamps column in Pivot Table

Now let's add time timestamps column in pivot table.
Schema::create('project_user', function (Blueprint $table) { $table->foreignId('project_id')->constrained(); $table->foreignId('user_id')->constrained(); $table->timestamps(); });
Then in the model we need to attach **withTimestamps()** in the model function.
public function users(): BelongsToMany { return $this->belongsToMany(User::class, 'project_user', 'project_id', 'user_id') ->withTimestamps(); }
And in the welcome.blade.php , you need to show created_at column.
@foreach ($projects as $project) <tr> <th>{{ $loop->index + 1 }}</th> <th>{{ $project->name }}</th> <td> <ul> @foreach ($project->users as $user) <li>{{ $user->name }}({{ $user->pivot->created_at }})</li> @endforeach </ul> </td> </tr> @endforeach
![Example Image of many-to-many relationship with withTimestamps!](http://127.0.0.1:8000/storage/uploads/images/C2yykmEpp2kmoavAD4J1E7jYPMAH1iS19KBuHESj.png) If you want to change pivot name from the loop, then you need to attach 1 more function in Project model.
public function users(): BelongsToMany { return $this->belongsToMany(User::class, 'projects_users', 'projects_id', 'users_id') ->withTimestamps() ->as('project_user'); }
Now your welcome.blade.php will look like this
@foreach ($projects as $project) <tr> <th>{{ $loop->index + 1 }}</th> <th>{{ $project->name }}</th> <td> <ul> @foreach ($project->users as $user) <li>{{ $user->name }} ({{ $user->project_user->is_manager }})</li> @endforeach </ul> </td> </tr> @endforeach
The output will be the same.

2 - Adding is_manager column in Pivot Table

Let's modify project_user table . Goto Migrations, add 1 more column in the project_user table. ```php Schema::create('project_user', function (Blueprint $table) { $table->foreignId('project_id')->constrained(); $table->foreignId('user_id')->constrained(); $table->boolean('is_manager')->default(false); $table->timestamps(); }); ``` Goto Seeder attaches manager to user in random order. ```php $projects = \App\Models\Project::factory(10)->create(); foreach ($projects as $project) { $users = User::inRandomOrder()->take(rand(1, 3))->pluck('id'); foreach ($users as $user) { $project->users()->attach($user,['is_manager'=>rand(0,1)]); } } ``` Goto your Project model, create a new relationship function named managers. ```php public function managers(): BelongsToMany { return $this->belongsToMany(User::class, 'projects_user', 'project_id', 'user_id') ->withTimestamps() ->withPivot(['is_manager']) ->as('project_user'); } ``` Now go to your controller and call managers function. ```php public function index() { $projects = Project::with('managers')->get(); $data = ['projects' => $projects]; return view('welcome', $data); } ``` Now go to **welcome.blade.php** ```php @foreach ($projects as $project) {{ $loop->index + 1 }} {{ $project->name }}
    @foreach ($project->managers as $manager)
  • {{ $manager->name }} ({{ $manager->project_user->is_manager }})
  • @endforeach
@endforeach ``` ![Example Image of many-to-many relationship with withTimestamps!](http://127.0.0.1:8000/storage/uploads/images/HAW5LrHW6wdVTiY5CkQUz3Ne2axehnsbmqwxrKNc.png) You haven't seen pivot model; I did not create a pivot model for projects and users. This is why we are not calling any relation from the pivot table. Now we will create a separate model for the pivot table. #### 3 - Adding Relationship(manager_id) with Pivot Table ```php php artisan make:model ProjectUser ``` ```php class ProjectUser extends Pivot { use HasFactory; public function manager(): BelongsTo { return $this->belongsTo(User::class, 'manager_id'); } } ``` In this model , we have extended this class from model to pivot and added a new relationship with managers. Now goto to **Database/Migration/project_user.php** ```php Schema::create('project_user', function (Blueprint $table) { $table->foreignId('project_id')->constrained(); $table->foreignId('user_id')->constrained(); $table->boolean('is_manager')->default(false); $table->timestamps(); }); ``` After that go to your **Project.php** Model and attach a new function in users relationship. ```php public function users(): BelongsToMany { return $this->belongsToMany(User::class, 'project_users', 'project_id', 'user_id') ->withTimestamps() ->withPivot('manager_id') ->using(ProjectUser::class); } ``` After that, goto to your **ProjectSeeder** and attach **manager_id** in random order. ```php $projects = \App\Models\Project::factory(10)->create(); foreach ($projects as $project) { $users = User::inRandomOrder()->take(rand(1, 3))->pluck('id'); foreach ($users as $user) { $project->users()->attach($user, ['manager_id' => User::inRandomOrder()->first()->id]); } } ``` Now goto your **HomeController** ```php public function index() { $projects = Project::with('users')->get(); $data = ['projects' => $projects]; return view('welcome', $data); } ``` and in the **welcome.blade.php** ```php @foreach ($projects as $project) {{ $loop->index + 1 }} {{ $project->name }}
    @foreach ($project->users as $user)
  • {{ $user->name }}({{ $user->pivot->manager->name }})
  • @endforeach
@endforeach ``` ![Example Image of many-to-many relationship with withTimestamps!](http://localhost:8000/storage/uploads/images/tMZsHprXygd8nQktM7CrqtCrO6HrQ2owZzhqdY4h.png) ![Example Image of Database many-to-many relationship!](http://localhost:8000/storage/uploads/images/6MJokJpggU7z3HWAORkUgbuo8bHXoCriruy4MMli.png)
Tags
Laravel Relationship Laravel Many To Many Migration Laravel Create Many To Many Record Laravel Many To Many Relation Laravel Many To Many Relationship With Pivot Table Laravel Many To Many Pivot Table Laravel Update Many To Many Pivot Table Laravel Many To Many Relationship With Pivot Table Example Laravel Many To Many Attach Laravel Many To Many Insert Pivot Laravel Many To Many Relationship With Extra Fields Many To Many Relation In Laravel Many To Many Relationship In Laravel Example Laravel Many-To-Many