Laravel provides powerful support for Many-to-Many Polymorphic Relationships, allowing a single table to be associated with multiple models through a pivot table.
What is a Many-to-Many Polymorphic Relationship?
A Many-to-Many Polymorphic Relationship allows a parent model to be associated with multiple instances of different child models through a pivot table, and vice versa.
In this post, we’ll look at an example where you have a “Tag” model that can be applied to both “Post” and “Video” models. Here, “Post” and “Video” are the parent classes, and “Tag” is the child class.
The following diagram illustrates how the database will look like:
Let’s get started!
Step 1: Create and Run the Migrations
First, create the migration files for each entity using the Artisan command:
php artisan make:migration create_tags_table --create=tags
php artisan make:migration create_posts_table --create=posts
php artisan make:migration create_videos_table --create=videos
php artisan make:migration create_taggables_table --create=taggables
Modify the migration files as follows:
For the tags
table migration:
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('tags', function (Blueprint $table) {
$table->id();
$table->string('name');
// Add other columns as needed
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('tags');
}
};
For the posts
table migration:
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('posts', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->string('content')->nullable();
// Add other columns as needed
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('posts');
}
};
For the videos
table migration:
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('videos', function (Blueprint $table) {
$table->id();
$table->string('title');
// Add other columns as needed
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('videos');
}
};
For the pivot table migration (taggables
):
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('taggables', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('tag_id');
$table->unsignedBigInteger('taggable_id');
$table->string('taggable_type');
});
}
public function down(): void
{
Schema::dropIfExists('taggables');
}
};
Step 2: Create the Models
Create the models for Tag, Post, and Video using the Artisan command:
php artisan make:model Tag
php artisan make:model Post
php artisan make:model Video
Step 3: Define the Many-to-Many Polymorphic Relationship
A Many-to-Many Polymorphic Relationship is defined by using the morphToMany()
method on the models that can have tags (like Post
and Video
) and the morphedByMany()
method on the Tag model.
Define the relationships as follows:
For the Tag
model:
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Tag extends Model
{
protected $fillable = ['name'];
public function posts()
{
return $this->morphedByMany(Post::class, 'taggable');
}
public function videos()
{
return $this->morphedByMany(Video::class, 'taggable');
}
}
For the Post
model:
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
protected $fillable = ['title', 'content'];
public function tags()
{
return $this->morphToMany(Tag::class, 'taggable');
}
}
For the Video
model:
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Video extends Model
{
protected $fillable = ['title'];
public function tags()
{
return $this->morphToMany(Tag::class, 'taggable');
}
}
Step 4: Creating Records
Now, you can create records while utilizing the Many-to-Many Polymorphic Relationships.
// Creating tags
$tag1 = Tag::create(['name' => 'Laravel']);
$tag2 = Tag::create(['name' => 'PHP']);
$tag3 = Tag::create(['name' => 'Database']);
// Creating posts
$post1 = Post::create([
'title' => 'Introduction to Laravel',
'content' => '...'
]);
$post2 = Post::create([
'title' => 'Laravel Best Practices',
'content' => '...'
]);
// Creating videos
$video1 = Video::create([
'title' => 'Laravel Tutorial'
]);
$video2 = Video::create([
'title' => 'Advanced PHP Concepts'
]);
// Attaching tags to posts and videos
$post1->tags()->attach([$tag1->id, $tag2->id]);
$post2->tags()->attach([$tag1->id, $tag3->id]);
$video1->tags()->attach([$tag1->id, $tag2->id]);
$video2->tags()->attach([$tag2->id, $tag3->id]);
After we ran this code our database will contain the following data:
Step 5: Retrieving Records
To retrieve records attached by Many-to-Many Polymorphic Relationships we can use the following:
// Retrieving tags for a post
$post = Post::find(1);
$tags = $post->tags;
// Retrieving posts for a tag
$tag = Tag::find(1);
$posts = $tag->posts;
// Retrieving tags for a video
$video = Video::find(1);
$tags = $video->tags;
// Retrieving videos for a tag
$tag = Tag::find(2);
$videos = $tag->videos;
That’s it! You’ve successfully defined and used Many-to-Many Polymorphic Relationship.
Further Examples of Many-To-Many Polymorphic Relationships
Parent Model 1 | Parent Model 2 | Child Model | Polymorphic Relationship |
User | Role | Permission | permissionable |
Article | Category | Tag | taggable |
Project | Team | Member | skillable |
Document | Video | Viewer | viewable |
Conclusion
In this blog post, we explored Many-to-Many Polymorphic Relationships in Laravel, which allow for flexible associations between multiple models using a single pivot table. We discussed how to set up the relationships and perform operations such as creating and retrieving records.
By understanding Many-to-Many Polymorphic Relationships, you can build more versatile and dynamic applications in Laravel. Happy coding!
References
This entry is part 7 of 9 in the series Laravel Eloquent Relationships
- How to Use a One-to-One Relationship in Laravel
- How to Use a One-to-Many Relationship in Laravel
- How to Use a Many-to-Many Relationship in Laravel
- Using Extra Fields in Pivot Table With Laravel belongsToMany
- How to Use Has-Many-Through Relationships in Laravel
- How to use One-to-Many Polymorphic Relationship in Laravel
- How to use Many-to-Many Polymorphic Relationship in Laravel
- How to Paginate a Model with Relationship in Laravel
- How to Use `with()` to Eager Load Relationship in Laravel
Great article Johan!
I’m glad you’ve found my post useful. Thank you for your kind words!
great job
Thanks Mohammad 🙂
Very very helpful article to understand many to many polymorphism. Thanks!
You’re welcome, I’m glad to help!