How to use Many-to-Many Polymorphic Relationship in Laravel

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:

Diagram Showing the Tables Involved in a Many-to-Many Polymorphic Relationship : tagstaggables, posts and videos

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:

database/migrations/2023_07_24_184914_create_tags_table.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('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:

database/migrations/2023_07_24_184915_create_posts_table.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('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:

database/migrations/2023_07_24_184915_create_videos_table.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('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):

database/migrations/2023_07_24_185538_create_taggables_table.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('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:

app/Models/Tag
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:

app/Models/Post
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:

app/Models/Video
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:

MySQL Command Line Client Showing Tables posts, videos, tags and taggables

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 1Parent Model 2Child ModelPolymorphic Relationship
UserRolePermissionpermissionable
ArticleCategoryTagtaggable
ProjectTeamMemberskillable
DocumentVideoViewerviewable
Table Showing Example Models Which Can Have a Many-To-Many Polymorphic Relationship

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

Johan van den Broek

Johan is the creator of laracoding.com. As a child, he began tinkering with various programming languages, many of which have been long forgotten today. Currently, he works exclusively with PHP and Laravel, and his passion for programming remains to this day.

6 thoughts on “How to use Many-to-Many Polymorphic Relationship in Laravel

    1. I’m glad you’ve found my post useful. Thank you for your kind words!

  1. Very very helpful article to understand many to many polymorphism. Thanks!

Leave a Reply

Your email address will not be published. Required fields are marked *

Recent Posts