Hello! If you're building a Laravel 12 application and need to create a many-to-many relationship between database tables, you're in the right place. In this beginner-friendly guide, I’ll walk you through setting up a many-to-many relationship using Laravel’s Eloquent ORM.
We’ll use a practical example: Users
who can have multiple Roles
, and Roles
that can belong to multiple Users
. By the end, you’ll know how to define models, migrations, a pivot table, and manage relationships.
In a many-to-many relationship, multiple records in one table can be associated with multiple records in another table. For example:
User
can have multiple Roles
(e.g., admin, editor).Role
can be assigned to multiple Users
.This is managed using a pivot table that connects the two tables (e.g., role_user
).
Before we begin, ensure you have:
If you don’t have a project, create one with:
laravel new example-app
We’ll create three tables:
users
: Stores user data.roles
: Stores role data.role_user
: A pivot table linking users and roles.Laravel includes a default users
migration. Check database/migrations
to ensure it exists and looks like this:
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('email')->unique();
$table->string('password');
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('users');
}
};
Run this command to create a migration for the roles
table:
php artisan make:migration create_roles_table
Open the generated migration file in database/migrations
and update it:
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('roles', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('roles');
}
};
Run this command to create a migration for the role_user
pivot table:
php artisan make:migration create_role_user_table
Open the generated migration file and update it:
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('role_user', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('user_id');
$table->unsignedBigInteger('role_id');
$table->timestamps();
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
$table->foreign('role_id')->references('id')->on('roles')->onDelete('cascade');
});
}
public function down(): void
{
Schema::dropIfExists('role_user');
}
};
Explanation:
role_user
table has user_id
and role_id
as foreign keys.onDelete('cascade')
ensures that if a user or role is deleted, their pivot table entries are removed.role_user
follows Laravel’s convention (alphabetical order of model names).Run the migrations to create the tables:
php artisan migrate
Define the User
and Role
models and set up their many-to-many relationships.
Open app/Models/User.php
and add the belongsToMany
relationship:
<?php
namespace App\Models;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
class User extends Authenticatable
{
protected $fillable = ['name', 'email', 'password'];
public function roles(): BelongsToMany
{
return $this->belongsToMany(Role::class);
}
}
Explanation: The belongsToMany
method defines that a User
can have múltiples Roles
.
Run this command to create the Role
model:
php artisan make:model Role
Open app/Models/Role.php
and add the belongsToMany
relationship:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
class Role extends Model
{
protected $fillable = ['name'];
public function users(): BelongsToMany
{
return $this->belongsToMany(User::class);
}
}
Explanation: The belongsToMany
method defines that a Role
can be assigned to multiple Users
.
Create a controller to test the many-to-many relationship and a route to access it.
Run:
php artisan make:controller UserController
Open app/Http/Controllers/UserController.php
and add:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\User;
use App\Models\Role;
class UserController extends Controller
{
public function index()
{
// Create roles
$admin = Role::create(['name' => 'Admin']);
$editor = Role::create(['name' => 'Editor']);
// Create a user
$user = User::create([
'name' => 'John Doe',
'email' => '[email protected]',
'password' => bcrypt('password'),
]);
// Assign roles to the user
$user->roles()->attach([$admin->id, $editor->id]);
// Retrieve the user with their roles
$userWithRoles = User::with('roles')->first();
return response()->json([
'user' => $userWithRoles->name,
'email' => $userWithRoles->email,
'roles' => $userWithRoles->roles->pluck('name'),
]);
}
}
Explanation:
Admin
and Editor
).attach
.with('roles')
to eager-load the roles.Open routes/web.php
and add:
<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\UserController;
Route::get('/test-relationship', [UserController::class, 'index'])->name('test.relationship');
Start your Laravel server:
php artisan serve
Visit http://localhost:8000/test-relationship
in your browser. You should see a JSON response like:
{
"user": "John Doe",
"email": "[email protected]",
"roles": [
"Admin",
"Editor"
]
}
This confirms the many-to-many relationship is working!
You can manage and access related data in various ways:
$user = User::find(1);
$user->roles()->attach([1, 2]); // Attach roles with IDs 1 and 2
$user->roles()->sync([1]); // Sync to keep only role ID 1, removing others
$user->roles()->detach([1]); // Remove role ID 1
$user = User::find(1);
$roles = $user->roles; // Returns a collection of Roles
foreach ($roles as $role) {
echo $role->name; // Outputs: Admin, Editor
}
$role = Role::find(1);
$users = $role->users; // Returns a collection of Users
foreach ($users as $user) {
echo $user->name; // Outputs: John Doe
}
Create a Blade file resources/views/user.blade.php
:
<!DOCTYPE html>
<html>
<head>
<title>Laravel 12 Many-to-Many Relationship</title>
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container mt-5">
<h3>{{ $user->name }}'s Roles</h3>
<p><strong>Email:</strong> {{ $user->email }}</p>
<h4>Roles:</h4>
<ul>
@foreach ($user->roles as $role)
<li>{{ $role->name }}</li>
@endforeach
</ul>
</div>
</body>
</html>
Update the UserController
to return the view:
public function index()
{
$user = User::with('roles')->first();
return view('user', compact('user'));
}
Setting up a many-to-many relationship in Laravel 12 is straightforward with Eloquent ORM! This tutorial showed you how to create migrations, define models, set up a pivot table, and manage relationships using a Users
and Roles
example.
Whether you’re building role-based access control or other complex relationships, this approach is flexible and powerful. I hope this guide was easy to follow and helps you implement many-to-many relationships in your Laravel projects.
Q1: What is a pivot table, and why is it needed?
A: A pivot table (e.g., role_user
) stores the relationships between two tables in a many-to-many relationship. It contains foreign keys linking both tables (e.g., user_id
and role_id
).
Q2: What’s the difference between attach
, sync
, and detach
?
A: attach
adds roles without removing existing ones. sync
sets the exact roles, removing any not listed. detach
removes specified roles.
Q3: Why use onDelete('cascade')
in the pivot table?
A: It ensures that if a user or role is deleted, their entries in the pivot table are also removed, preventing orphaned records.
Q4: How do I add extra fields to the pivot table?
A: Add columns to the pivot table migration (e.g., assigned_at
for a timestamp). In the model, use withPivot
:
public function roles(): BelongsToMany
{
return $this->belongsToMany(Role::class)->withPivot('assigned_at');
}
Access it like $user->roles->first()->pivot->assigned_at
.
Q5: How do I check if a user has a specific role?
A: Use $user->roles->contains('name', 'Admin')
or query: $user->roles()->where('name', 'Admin')->exists()
.
You might also like: