Sling Academy
Home/PHP/Polymorphic Relations in Eloquent: Explained with Examples

Polymorphic Relations in Eloquent: Explained with Examples

Last updated: January 16, 2024

Introduction

Polymorphic relations in Laravel’s Eloquent ORM provide a way to set up a single association to different models. This guide walks you through the concept of polymorphic relationships, showing code examples and explaining how they can be used in your Laravel applications.

Understanding Polymorphic Relations

In Eloquent, relationships allowing a model to belong to more than one other model on a single association is known as a polymorphic relationship. Typical examples include things like comments or files that could be associated with posts, videos, or any other type of model. To understand this, let’s begin with a basic example: setting up a polymorphic relationship between a Comment model and models Post and Video.

class Comment extends Model
{
    public function commentable()
    {
        return $this->morphTo();
    }
}

class Post extends Model
{
    public function comments()
    {
        return $this->morphMany('App\Comment', 'commentable');
    }
}

class Video extends Model
{
    public function comments()
    {
        return $this->morphMany('App\Comment', 'commentable');
    }
}

In the database, your comments table might have a schema like this:

Schema::create('comments', function (Blueprint $table) {
    $table->id();
    $table->text('body');
    $table->morphs('commentable');
    $table->timestamps();
});

This will create two additional columns: commentable_type and commentable_id. The commentable_type column will store the class name of the related model and commentable_id stores the id of the related record. Using this setup, you can now post comments on any model that you set up a polymorphic relation with.

Retrieving And Using Polymorphic Relations

Let’s now see how to retrieve and work with this relationship. For example, to fetch comments related to a post:

$post = Post::find(1);
foreach ($post->comments as $comment) {
  echo $comment->body;
}

To do the same for videos:

$video = Video::find(1);
foreach ($video->comments as $comment) {
  echo $comment->body;
}

Advanced Relations: Morph Maps and Custom Keys

Laravel allows you to use custom names for the type field by using morph maps. This can help avoid fully-qualified class names being stored in your database. Let’s look at how to define and utilize a morph map:

use Illuminate\Database\Eloquent\Relations\Relation;

Relation::morphMap([
  'posts' => 'App\Post',
  'videos' => 'App\Video',
]);

Now, instead of Laravel storing the full model class name in the commentable_type, it will store the custom key from the morph map. Custom keys can also be set for IDs:

return $this->morphMany('App\Comment', 'commentable', 'model_type', 'model_id');

This changes the name of the columns Laravel looks for, from the default commentable_type and commentable_id to model_type and model_id.

Many-to-Many Polymorphic Relations

Eloquent also supports a ‘morphed by many’ relationship, which allows you to have a many-to-many polymorphic relation. This is typically used for scenarios like tags, which can belong to any number of different models. The setup looks like this:

class Tag extends Model
{
    public function posts()
    {
        return $this->morphedByMany('App\Post', 'taggable');
    }
    public function videos()
    {
        return $this->morphedByMany('App\Video', 'taggable');
    }
}

class Post extends Model
{
    public function tags()
    {
        return $this->morphToMany('App\Tag', 'taggable');
    }
}

class Video extends Model
{
    public function tags()
    {
        return $this->morphToMany('App\Tag', 'taggable');
    }
}

For storing data, you’ll need an intermediate table that might be named taggables, with a schema like this:

Schema::create('taggables', function (Blueprint $table) {
    $table->id();
    $table->morphs('taggable');
    $table->unsignedBigInteger('tag_id');
    $table->timestamps();
});

To retrieve all the tags associated with a post, you can simply use:

$post = Post::find(1);
foreach ($post->tags as $tag) {
  echo $tag->name;
}

Many-to-Many Polymorphic Relations: Custom Pivot Attributes

Custom pivot attributes can be accessed or assigned on a many-to-many polymorphic relation much in the same way as a standard many-to-many relation. This means you can have additional data about the relation in the pivot table and interact with it conveniently:

// When attaching a tag
$post->tags()->attach($tagId, ['custom_attribute' => 'value']);

// When retrieving pivot data
foreach ($post->tags as $tag) {
  echo $tag->pivot->custom_attribute;
}

Conclusion

Polymorphic relations in Eloquent are a powerful and flexible feature allowing for complex data structures and relationships to be elegantly managed within a Laravel application. They allow for more reusable and abstracted code and can significantly simplify your database design.

Next Article: Eloquent: Convert query result to PHP array

Previous Article: Mutators and Accessors in Eloquent: A Practical Guide

Series: Laravel & Eloquent Tutorials

PHP

You May Also Like

  • Pandas DataFrame.value_counts() method: Explained with examples
  • Constructor Property Promotion in PHP: Tutorial & Examples
  • Understanding mixed types in PHP (5 examples)
  • Union Types in PHP: A practical guide (5 examples)
  • PHP: How to implement type checking in a function (PHP 8+)
  • Symfony + Doctrine: Implementing cursor-based pagination
  • Laravel + Eloquent: How to Group Data by Multiple Columns
  • PHP: How to convert CSV data to HTML tables
  • Using ‘never’ return type in PHP (PHP 8.1+)
  • Nullable (Optional) Types in PHP: A practical guide (5 examples)
  • Explore Attributes (Annotations) in Modern PHP (5 examples)
  • An introduction to WeakMap in PHP (6 examples)
  • Type Declarations for Class Properties in PHP (5 examples)
  • Static Return Type in PHP: Explained with examples
  • PHP: Using DocBlock comments to annotate variables
  • PHP: How to ping a server/website and get the response time
  • PHP: 3 Ways to Get City/Country from IP Address
  • PHP: How to find the mode(s) of an array (4 examples)
  • PHP: Calculate standard deviation & variance of an array