Discussion Laravel PHP

Laravel Polymorphic Relationships

A polymorphic relationship allows the target model to belong to more than one type of model using a single association.

One To One (Polymorphic)

Table Structure

A one-to-one polymorphic relation is similar to a simple one-to-one relation; however, the target model can belong to more than one type of model on a single association. For example, a blog Post and a User may share a polymorphic relation to an Image model. Using a one-to-one polymorphic relation allows you to have a single list of unique images that are used for both blog posts and user accounts. First, let’s examine the table structure:

posts
    id - integer
    name - string

users
    id - integer
    name - string

images
    id - integer
    url - string
    imageable_id - integer
    imageable_type - string

Take note of the imageable_id and imageable_type columns on the images table. The imageable_id column will contain the ID value of the post or user, while the imageable_typecolumn will contain the class name of the parent model. The imageable_type column is used by Eloquent to determine which “type” of parent model to return when accessing the imageablerelation.

Model Structure

Next, let’s examine the model definitions needed to build this relationship:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Image extends Model
{
    /**
     * Get all of the owning imageable models.
     */
    public function imageable()
    {
        return $this->morphTo();
    }
}

class Post extends Model
{
    /**
     * Get the post's image.
     */
    public function image()
    {
        return $this->morphOne('App\Image', 'imageable');
    }
}

class User extends Model
{
    /**
     * Get the user's image.
     */
    public function image()
    {
        return $this->morphOne('App\Image', 'imageable');
    }
}

Retrieving The Relationship

Once your database table and models are defined, you may access the relationships via your models. For example, to retrieve the image for a post, we can use the image dynamic property:

$post = App\Post::find(1);

$image = $post->image;

You may also retrieve the parent from the polymorphic model by accessing the name of the method that performs the call to morphTo. In our case, that is the imageable method on the Image model. So, we will access that method as a dynamic property:

$image = App\Image::find(1);

$imageable = $image->imageable;

The imageable relation on the Image model will return either a Post or User instance, depending on which type of model owns the image.

One To Many (Polymorphic)

Table Structure

A one-to-many polymorphic relation is similar to a simple one-to-many relation; however, the target model can belong to more than one type of model on a single association. For example, imagine users of your application can “comment” on both posts and videos. Using polymorphic relationships, you may use a single comments table for both of these scenarios. First, let’s examine the table structure required to build this relationship:

posts
    id - integer
    title - string
    body - text

videos
    id - integer
    title - string
    url - string

comments
    id - integer
    body - text
    commentable_id - integer
    commentable_type - string

Model Structure

Next, let’s examine the model definitions needed to build this relationship:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Comment extends Model
{
    /**
     * Get all of the owning commentable models.
     */
    public function commentable()
    {
        return $this->morphTo();
    }
}

class Post extends Model
{
    /**
     * Get all of the post's comments.
     */
    public function comments()
    {
        return $this->morphMany('App\Comment', 'commentable');
    }
}

class Video extends Model
{
    /**
     * Get all of the video's comments.
     */
    public function comments()
    {
        return $this->morphMany('App\Comment', 'commentable');
    }
}

Retrieving The Relationship

Once your database table and models are defined, you may access the relationships via your models. For example, to access all of the comments for a post, we can use the commentsdynamic property:

$post = App\Post::find(1);

foreach ($post->comments as $comment) {
    //
}

You may also retrieve the owner of a polymorphic relation from the polymorphic model by accessing the name of the method that performs the call to morphTo. In our case, that is the commentable method on the Comment model. So, we will access that method as a dynamic property:

$comment = App\Comment::find(1);

$commentable = $comment->commentable;

The commentable relation on the Comment model will return either a Post or Video instance, depending on which type of model owns the comment.

Many To Many (Polymorphic)

Table Structure

Many-to-many polymorphic relations are slightly more complicated than morphOne and morphMany relationships. For example, a blog Post and Video model could share a polymorphic relation to a Tag model. Using a many-to-many polymorphic relation allows you to have a single list of unique tags that are shared across blog posts and videos. First, let’s examine the table structure:

posts
    id - integer
    name - string

videos
    id - integer
    name - string

tags
    id - integer
    name - string

taggables
    tag_id - integer
    taggable_id - integer
    taggable_type - string

Model Structure

Next, we’re ready to define the relationships on the model. The Post and Video models will both have a tags method that calls the morphToMany method on the base Eloquent class:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    /**
     * Get all of the tags for the post.
     */
    public function tags()
    {
        return $this->morphToMany('App\Tag', 'taggable');
    }
}

Defining The Inverse Of The Relationship

Next, on the Tag model, you should define a method for each of its related models. So, for this example, we will define a posts method and a videos method:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Tag extends Model
{
    /**
     * Get all of the posts that are assigned this tag.
     */
    public function posts()
    {
        return $this->morphedByMany('App\Post', 'taggable');
    }

    /**
     * Get all of the videos that are assigned this tag.
     */
    public function videos()
    {
        return $this->morphedByMany('App\Video', 'taggable');
    }
}

Retrieving The Relationship

Once your database table and models are defined, you may access the relationships via your models. For example, to access all of the tags for a post, you can use the tags dynamic property:

$post = App\Post::find(1);

foreach ($post->tags as $tag) {
    //
}

You may also retrieve the owner of a polymorphic relation from the polymorphic model by accessing the name of the method that performs the call to morphedByMany. In our case, that is the posts or videos methods on the Tag model. So, you will access those methods as dynamic properties:

$tag = App\Tag::find(1);

foreach ($tag->videos as $video) {
    //
}

Custom Polymorphic Types

By default, Laravel will use the fully qualified class name to store the type of the related model. For instance, given the one-to-many example above where a Comment may belong to a Post or a Video, the default commentable_type would be either App\Post or App\Video, respectively. However, you may wish to decouple your database from your application’s internal structure. In that case, you may define a “morph map” to instruct Eloquent to use a custom name for each model instead of the class name:

use Illuminate\Database\Eloquent\Relations\Relation;

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

You may register the morphMap in the boot function of your AppServiceProvider or create a separate service provider if you wish.

You may also like...

Leave a Reply

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