
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.
{{ $loop->index + 1 }}
{{ $project->name }}
@endforeach
```

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 }}
@endforeach
```


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>

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

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)-
@foreach ($project->managers as $manager)
- {{ $manager->name }} ({{ $manager->project_user->is_manager }}) @endforeach
-
@foreach ($project->users as $user)
- {{ $user->name }}({{ $user->pivot->manager->name }}) @endforeach