Laravel 12 Many-to-Many Relationship: A Complete Guide with Examples
Many-to-many relationships are common in web applications when multiple records in one table are associated with multiple records in another table. Laravel 12 makes working with these relationships simple through its Eloquent ORM.
Real-World Examples
- Students and Courses (a student can enroll in many courses, and a course can have many students)
- Posts and Tags (a post can have many tags, and a tag can belong to many posts)
In this comprehensive guide, we'll cover:
- Understanding Many-to-Many Relationships
- Database Structure for Many-to-Many
- Creating Migrations and Models
- Defining Relationships in Eloquent
- Attaching, Detaching, and Syncing Relationships
- Eager Loading for Optimization
- Practical Example: Blog Post Tagging System
1. Understanding Many-to-Many Relationships
A many-to-many relationship requires a pivot table (junction table) that connects the two related tables.
Example Structure
poststable:id,title,contenttagstable:id,namepost_tagpivot table:post_id,tag_id
Each post can have multiple tags, and each tag can be assigned to multiple posts.
2. Setting Up the Database
Step 1: Create Laravel 12 Project
composer create-project laravel/laravel many-to-many-demo
cd many-to-many-demo
Step 2: Configure Database
Edit your .env file with database credentials.
3. Creating Migrations and Models
A. Create Posts Table
php artisan make:migration create_posts_table
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->text('content');
$table->timestamps();
});
B. Create Tags Table
php artisan make:migration create_tags_table
Schema::create('tags', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->timestamps();
});
C. Create Pivot Table (post_tag)
php artisan make:migration create_post_tag_table
Schema::create('post_tag', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('post_id');
$table->unsignedBigInteger('tag_id');
$table->timestamps();
$table->foreign('post_id')->references('id')->on('posts')->onDelete('cascade');
$table->foreign('tag_id')->references('id')->on('tags')->onDelete('cascade');
});
Run migrations:
php artisan migrate
D. Create Models
php artisan make:model Post
php artisan make:model Tag
4. Defining Many-to-Many Relationships
In Post Model
public function tags()
{
return $this->belongsToMany(Tag::class);
}
In Tag Model
public function posts()
{
return $this->belongsToMany(Post::class);
}
5. Working with Relationships
A. Attaching Tags to a Post
$post = Post::find(1);
$post->tags()->attach([1, 2, 3]); // Attach tags with IDs 1, 2, and 3
B. Detaching Tags
$post->tags()->detach(2); // Detach tag with ID 2
$post->tags()->detach(); // Detach all tags
C. Syncing Tags (Maintains exact list)
$post->tags()->sync([1, 3]); // Only tags 1 and 3 will remain attached
D. Toggle Tags
$post->tags()->toggle([1, 2]); // Toggles attachment status
E. Adding Extra Pivot Data
$post->tags()->attach(1, ['created_by' => Auth::id()]);
6. Retrieving Data
A. Get All Tags for a Post
$post = Post::with('tags')->find(1);
foreach ($post->tags as $tag) {
echo $tag->name;
}
B. Get All Posts for a Tag
$tag = Tag::with('posts')->find(1);
foreach ($tag->posts as $post) {
echo $post->title;
}
C. Eager Loading
// Without eager loading (N+1 problem)
$posts = Post::all();
foreach ($posts as $post) {
foreach ($post->tags as $tag) {
// ...
}
}
// With eager loading
$posts = Post::with('tags')->get();
7. Practical Example: Blog Post Tagging System
Step 1: Create Sample Data
php artisan tinker
// Create posts
Post::create(['title' => 'First Post', 'content' => 'Content...']);
Post::create(['title' => 'Second Post', 'content' => 'Content...']);
// Create tags
Tag::create(['name' => 'Laravel']);
Tag::create(['name' => 'PHP']);
Tag::create(['name' => 'Web Development']);
// Attach tags
$post = Post::first();
$post->tags()->attach([1, 2]);
Step 2: Create Routes
Route::get('/posts', function () {
$posts = Post::with('tags')->get();
return view('posts.index', compact('posts'));
});
Step 3: Create Blade View
<!-- resources/views/posts/index.blade.php -->
@foreach ($posts as $post)
<h2>{{ $post->title }}</h2>
<p>Tags:
@foreach ($post->tags as $tag)
<span class="badge bg-primary">{{ $tag->name }}</span>
@endforeach
</p>
@endforeach
Step 4: Test in Browser
php artisan serve
Visit http://localhost:8000/posts to see posts with their tags.
Conclusion
✅ Key Takeaways:
- Many-to-many relationships require a pivot table
- Use
belongsToMany()in both models - Methods like
attach(),detach(), andsync()manage relationships - Eager loading prevents N+1 query problems
Next Steps:
- Explore polymorphic many-to-many relationships
- Learn about custom pivot models
- Implement tag filtering in your blog
🚀 Happy Coding!
📌 Questions? Drop them in the comments below!
Projects to explore next
Source-code projects from the KritiMyantra catalogue. Check each project's details before buying.
Flask Authentication System (Free Version) - Kritim Yantra
🛠 Features✔ Google & GitHub Authentication✔ User Registration & Login✔ Profile/Dashboard Access✔ Secure & Mi...
Flask Blog CMS with Admin Dashboard — Python Source Code
A Flask-based blogging website with a built-in admin dashboard. Manage articles, images, categories, tags and featured p...
Laravel 12 + ReactJS Starter Kit: Add Multi-Language Support (Arabic, English, Spanish) with Blog CRUD
🚀 Installation & Setup Download & Extract Get the ZIP from kritimyantra.com and extract it. Includes preconfigu...
Comments
No comments yet. Be the first to comment.
Sign in to join the discussion.