search
HomeBackend DevelopmentPHP TutorialRe-Introducing Eloquent's Polymorphic Relationships

Re-Introducing Eloquent's Polymorphic Relationships

Core points

  • Laravel's polymorphic association allows a model to belong to multiple other models on one association. This simplifies the database structure, makes the code easier to maintain, and allows for more dynamic and flexible data relationships.
  • Setting up polymorphic associations in Laravel involves defining associations in the Eloquent model. The morphTo method is used to receive a polymorphic associated model, while the morphMany or morphOne method is used to a model associated with other models.
  • Laravel's MorphMap method can be used to instruct Eloquent to use a custom name instead of a class name for each model. This is helpful in case of model namespace changes or namespaces that are too long.
  • Laravel supports many-to-many polymorphic relationships, allowing a model to belong to multiple models on a many-to-many basis. This is especially useful in complex applications.

This article was peer-reviewed by Younes Rafie. Thanks to all the peer reviewers of SitePoint to make the content of SitePoint perfect!


Re-Introducing Eloquent's Polymorphic Relationships

You may have used different types of relationships between models or database tables, such as common in Laravel: one-to-one, one-to-many, many-to-many, and has-many-through. But there is another less common type: polymorphic association. So what is polymorphic association?

Polymorphic association refers to a model that can belong to multiple other models in one association.

To illustrate this, let's create a fictional scene where we have Topic and Post models. Users can leave comments in topics and posts. Using polymorphic relationships, we can use a single comments table for both cases. Surprisingly, right? This seems a bit impractical because ideally we have to create the post_comments table and the topic_comments table to distinguish comments. Using polymorphic relationships, we do not need two tables. Let's understand polymorphic relationships through a practical example.

What we will build

We will create a demo music app that contains songs and albums. In this app, we can like songs and albums. Using polymorphic relationships, we will use a single upvotes table for both cases. First, let's examine the table structure required to build this relationship:

<code>albums
    id - integer
    name - string

songs
    id - integer
    title - string
    album_id - integer

upvotes
    id - integer
    upvoteable_id - integer
    upvoteable_type - string
</code>

Let's discuss the upvoteable_id and upvoteable_type columns, which may seem a little strange to those who have not used polymorphic relationships before. The upvoteable_id column will contain the ID value of the album or song, while the upvoteable_type column will contain the class name that owns the model. The upvoteable_type column is how the ORM determines which "type" to return to own the model when accessing the upvoteable relationship.

Generate models and migration

I assume you already have a running Laravel application. If not, this advanced quick-start course may be helpful. Let's first create three models and migrations, and then edit the migrations to suit our needs.

<code>albums
    id - integer
    name - string

songs
    id - integer
    title - string
    album_id - integer

upvotes
    id - integer
    upvoteable_id - integer
    upvoteable_type - string
</code>

Note that passing the -m flag when creating a model will also generate migrations associated with these models. Let's adjust the up methods in these migrations to get the desired table structure:

{some_timestamp}_create_albums_table.php

<code>php artisan make:model Album -m
php artisan make:model Song -m
php artisan make:model Upvote -m
</code>

{some_timestamp}_create_songs_table.php

public function up()
{
    Schema::create('albums', function (Blueprint $table) {
       $table->increments('id');
        $table->string('name');
        $table->timestamps();
    });
}

{some_timestamp}_create_upvotes_table.php

public function up()
{
    Schema::create('songs', function (Blueprint $table) {
        $table->increments('id');
        $table->string('title');
        $table->integer('album_id')->unsigned()->index();
        $table->timestamps();

        $table->foreign('album_id')->references('id')->on('albums')->onDelete('cascade');
    });
}

Now, we can run the artisan migrate command to create these three tables:

public function up()
{
    Schema::create('upvotes', function (Blueprint $table) {
        $table->increments('id');
        $table->morphs('upvoteable'); // 添加无符号整数 upvoteable_id 和字符串 upvoteable_type
        $table->timestamps();
    });
}

Let's configure our model to pay attention to the polymorphic relationship between albums, songs and likes:

app/Upvote.php

<code>php artisan migrate</code>

app/Album.php

[...]
class Upvote extends Model
{
    /**
     * 获取所有拥有模型。
     */
    public function upvoteable()
    {
        return $this->morphTo();
    }
}

app/Song.php

class Album extends Model
{
    protected $fillable = ['name'];

    public function songs()
    {
        return $this->hasMany(Song::class);
    }

    public function upvotes()
    {
        return $this->morphMany(Upvote::class, 'upvoteable');
    }
}
The

method in the upvotes Album and Song models defines the polymorphic one-to-many relationship between these models and the Upvote model and will help us get all the likes of that particular model instance.

After defining the relationship, we can now try the application to better understand how polymorphic relationships work. We won't create any views for this app, we will only try our app from the console.

If you consider the controller and where we should place the like method, I suggest creating an AlbumUpvoteController and a SongUpvoteController. In this way, when we deal with polymorphic relationships, we can strictly connect things to the object we are operating on. In our case, we can like the album and song. Likes are not an album or a song. Also, it's not a general like, which is the opposite of how we have UpvotesController in most one-to-many relationships. Hope this makes sense.

Let's start the console:

class Song extends Model
{
    protected $fillable = ['title', 'album_id'];

    public function album()
    {
        return $this->belongsTo(Album::class);
    }

    public function upvotes()
    {
        return $this->morphMany(Upvote::class, 'upvoteable');
    }
}

Retrieval Relationship

Now that we have some data ready, we can access our relationships through our model. Here is a screenshot of the data in the upvotes table:

Re-Introducing Eloquent's Polymorphic Relationships

To access all the likes of the album, we can use upvotes Dynamic attribute:

<code>php artisan tinker
>>> $album = App\Album::create(['name' => 'More Life']);
>>> $song = App\Song::create(['title' => 'Free smoke', 'album_id' => 1]);
>>> $upvote1 = new App\Upvote;
>>> $upvote2 = new App\Upvote;
>>> $upvote3 = new App\Upvote;
>>> $album->upvotes()->save($upvote1);
>>> $song->upvotes()->save($upvote2);
>>> $album->upvotes()->save($upvote3);</code>

can also retrieve the owner of a polymorphic relationship from the polymorphic model by accessing the name of the method that performs the call to morphTo. In our case, that is the upvoteable method on the Upvote model. Therefore, we will access the method as a dynamic property:

$album = App\Album::find(1);
$upvotes = $album->upvotes;
$upvotescount = $album->upvotes->count();
The

relation on the upvoteableUpvote model will return an Album instance, as this like is owned by the instance of the Album instance.

Because we can get the number of likes for a song or album, we can sort the song or album based on the likes on the view. That's how music charts work.

For songs, we will get likes like this:

<code>albums
    id - integer
    name - string

songs
    id - integer
    title - string
    album_id - integer

upvotes
    id - integer
    upvoteable_id - integer
    upvoteable_type - string
</code>

Custom polymorphic type

By default, Laravel will use fully qualified class names to store the types of the relevant model. For example, in the above example, Upvote might belong to Album or Song, and the default upvoteable_type is App\Album or App\Song respectively.

However, this has a big drawback. What if the namespace of the Album model changes? We will have to do some sort of migration to rename all occurrences in the upvotes table. This is a little tricky! What happens if the namespace is long (e.g. App\Models\Data\Topics\Something\SomethingElse)? This means we have to set a very long maximum length on the column. This is where the MorphMap method can help us.

The "morphMap" method will instruct Eloquent to use a custom name for each model, not the class name:

<code>php artisan make:model Album -m
php artisan make:model Song -m
php artisan make:model Upvote -m
</code>

We can register morphMap in the boot function of the AppServiceProvider, or create a separate service provider. In order for the new changes to take effect, we must run the composer dump-autoload command. So now we can add this new like record:

public function up()
{
    Schema::create('albums', function (Blueprint $table) {
       $table->increments('id');
        $table->string('name');
        $table->timestamps();
    });
}

It behaves exactly the same as the previous example.

Conclusion

Even if you may have never encountered a situation where you need to use polymorphic relationships, that day may eventually come. The advantage of using Laravel is that handling this situation is very easy and it takes no need to do any model association tricks to get things working. Laravel even supports many-to-many polymorphic relationships. You can read more about it here.

I hope you have now understood polymorphic relationships and what may be needed for these types of relationships. Another slightly more advanced example of polymorphic relationships is here. If you find this helpful, please share it with your friends and don't forget to click the Like button. Feel free to leave your thoughts in the comment section below!

FAQs about Eloquent's polymorphic relationships

What are the benefits of using polymorphic relationships in Laravel?

Polymorphic relations in Laravel provide a flexible and efficient way to process related data between different models. They allow a model to belong to multiple other types of models in one association. This means you can have a single unique identifier list of all relevant data regardless of their type. This can greatly simplify your database structure and make your code easier to maintain. It also allows for more dynamic and flexible data relationships, which are particularly useful in complex applications.

How to set polymorphic relationships in Laravel?

Setting up polymorphic relationships in Laravel involves defining relationships in the Eloquent model. First, you need to define the relationship on the model that will receive the polymorphic relationship. This is done using the morphTo method. Then, on the model that will be associated with the other models, use the morphMany or morphOne method according to whether the relationship is one-to-many or one-to-one.

Can you provide an example of polymorphic relationships in Laravel?

Of course, let's consider a blog platform where both posts and users can have comments. In this case, the Comment model will have a polymorphic relationship with the Post and User models. Here is how to define this relationship:

<code>albums
    id - integer
    name - string

songs
    id - integer
    title - string
    album_id - integer

upvotes
    id - integer
    upvoteable_id - integer
    upvoteable_type - string
</code>

You can retrieve relevant records in polymorphic relationships like you would with any other Eloquent relationship. For example, if you want to retrieve all comments from a post, you can do this:

<code>php artisan make:model Album -m
php artisan make:model Song -m
php artisan make:model Upvote -m
</code>

Save related records in polymorphic relationships also similar to other Eloquent relationships. You can associate the model using the associate method and save the model. Here is an example:

public function up()
{
    Schema::create('albums', function (Blueprint $table) {
       $table->increments('id');
        $table->string('name');
        $table->timestamps();
    });
}

What are some common use cases for polymorphic relationships?

Polymorphic relations are particularly useful in situations where the model can belong to multiple other types of models. Some common use cases include comments that can belong to posts and users, tags that can be applied to multiple types of content, and images or files that can be attached to various types of entities.

What are the limitations or disadvantages of using polymorphic relationships?

While polymorphic relationships provide a lot of flexibility, they may also be more complex and harder to set up and manage than standard Eloquent relationships. They also support all features of standard relationships, such as desire to load constraints.

How to delete relevant records in polymorphic relationships?

You can use the delete method on the relationship to delete relevant records in a polymorphic relationship. For example, to delete all comments from a post, you can do this:

public function up()
{
    Schema::create('songs', function (Blueprint $table) {
        $table->increments('id');
        $table->string('title');
        $table->integer('album_id')->unsigned()->index();
        $table->timestamps();

        $table->foreign('album_id')->references('id')->on('albums')->onDelete('cascade');
    });
}

Can I use polymorphic relationships with many-to-many relationships?

Yes, Laravel supports many-to-many polymorphic relationships through the morphToMany and morphedByMany methods. This allows a model to belong to multiple models on a many-to-many basis.

How to deal with polymorphic relationships in database migration?

In a database migration, you usually add two columns to the table that will receive polymorphic relationships: one for the related model ID and the other for the related model type. Laravel provides a convenient morphs method to add these columns:

public function up()
{
    Schema::create('upvotes', function (Blueprint $table) {
        $table->increments('id');
        $table->morphs('upvoteable'); // 添加无符号整数 upvoteable_id 和字符串 upvoteable_type
        $table->timestamps();
    });
}

This adds commentable_id and commentable_type columns to the table.

The above is the detailed content of Re-Introducing Eloquent's Polymorphic Relationships. For more information, please follow other related articles on the PHP Chinese website!

Statement
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
What is the difference between absolute and idle session timeouts?What is the difference between absolute and idle session timeouts?May 03, 2025 am 12:21 AM

Absolute session timeout starts at the time of session creation, while an idle session timeout starts at the time of user's no operation. Absolute session timeout is suitable for scenarios where strict control of the session life cycle is required, such as financial applications; idle session timeout is suitable for applications that want users to keep their session active for a long time, such as social media.

What steps would you take if sessions aren't working on your server?What steps would you take if sessions aren't working on your server?May 03, 2025 am 12:19 AM

The server session failure can be solved through the following steps: 1. Check the server configuration to ensure that the session is set correctly. 2. Verify client cookies, confirm that the browser supports it and send it correctly. 3. Check session storage services, such as Redis, to ensure that they are running normally. 4. Review the application code to ensure the correct session logic. Through these steps, conversation problems can be effectively diagnosed and repaired and user experience can be improved.

What is the significance of the session_start() function?What is the significance of the session_start() function?May 03, 2025 am 12:18 AM

session_start()iscrucialinPHPformanagingusersessions.1)Itinitiatesanewsessionifnoneexists,2)resumesanexistingsession,and3)setsasessioncookieforcontinuityacrossrequests,enablingapplicationslikeuserauthenticationandpersonalizedcontent.

What is the importance of setting the httponly flag for session cookies?What is the importance of setting the httponly flag for session cookies?May 03, 2025 am 12:10 AM

Setting the httponly flag is crucial for session cookies because it can effectively prevent XSS attacks and protect user session information. Specifically, 1) the httponly flag prevents JavaScript from accessing cookies, 2) the flag can be set through setcookies and make_response in PHP and Flask, 3) Although it cannot be prevented from all attacks, it should be part of the overall security policy.

What problem do PHP sessions solve in web development?What problem do PHP sessions solve in web development?May 03, 2025 am 12:02 AM

PHPsessionssolvetheproblemofmaintainingstateacrossmultipleHTTPrequestsbystoringdataontheserverandassociatingitwithauniquesessionID.1)Theystoredataserver-side,typicallyinfilesordatabases,anduseasessionIDstoredinacookietoretrievedata.2)Sessionsenhances

What data can be stored in a PHP session?What data can be stored in a PHP session?May 02, 2025 am 12:17 AM

PHPsessionscanstorestrings,numbers,arrays,andobjects.1.Strings:textdatalikeusernames.2.Numbers:integersorfloatsforcounters.3.Arrays:listslikeshoppingcarts.4.Objects:complexstructuresthatareserialized.

How do you start a PHP session?How do you start a PHP session?May 02, 2025 am 12:16 AM

TostartaPHPsession,usesession_start()atthescript'sbeginning.1)Placeitbeforeanyoutputtosetthesessioncookie.2)Usesessionsforuserdatalikeloginstatusorshoppingcarts.3)RegeneratesessionIDstopreventfixationattacks.4)Considerusingadatabaseforsessionstoragei

What is session regeneration, and how does it improve security?What is session regeneration, and how does it improve security?May 02, 2025 am 12:15 AM

Session regeneration refers to generating a new session ID and invalidating the old ID when the user performs sensitive operations in case of session fixed attacks. The implementation steps include: 1. Detect sensitive operations, 2. Generate new session ID, 3. Destroy old session ID, 4. Update user-side session information.

See all articles

Hot AI Tools

Undresser.AI Undress

Undresser.AI Undress

AI-powered app for creating realistic nude photos

AI Clothes Remover

AI Clothes Remover

Online AI tool for removing clothes from photos.

Undress AI Tool

Undress AI Tool

Undress images for free

Clothoff.io

Clothoff.io

AI clothes remover

Video Face Swap

Video Face Swap

Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Tools

Notepad++7.3.1

Notepad++7.3.1

Easy-to-use and free code editor

MantisBT

MantisBT

Mantis is an easy-to-deploy web-based defect tracking tool designed to aid in product defect tracking. It requires PHP, MySQL and a web server. Check out our demo and hosting services.

VSCode Windows 64-bit Download

VSCode Windows 64-bit Download

A free and powerful IDE editor launched by Microsoft

EditPlus Chinese cracked version

EditPlus Chinese cracked version

Small size, syntax highlighting, does not support code prompt function

SublimeText3 Mac version

SublimeText3 Mac version

God-level code editing software (SublimeText3)