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 themorphMany
ormorphOne
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!
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'); } }
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:
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();
relation on the upvoteable
Upvote 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>
How to use polymorphic relationships to retrieve related records?
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>
How to save related records using polymorphic relationships?
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!

Hot AI Tools

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

Undress AI Tool
Undress images for free

Clothoff.io
AI clothes remover

AI Hentai Generator
Generate AI Hentai for free.

Hot Article

Hot Tools

Notepad++7.3.1
Easy-to-use and free code editor

SublimeText3 Chinese version
Chinese version, very easy to use

Zend Studio 13.0.1
Powerful PHP integrated development environment

Dreamweaver CS6
Visual web development tools

SublimeText3 Mac version
God-level code editing software (SublimeText3)

Hot Topics



The PHP Client URL (cURL) extension is a powerful tool for developers, enabling seamless interaction with remote servers and REST APIs. By leveraging libcurl, a well-respected multi-protocol file transfer library, PHP cURL facilitates efficient execution of various network protocols, including HTTP, HTTPS, and FTP. This extension offers granular control over HTTP requests, supports multiple concurrent operations, and provides built-in security features.

Alipay PHP...

Article discusses late static binding (LSB) in PHP, introduced in PHP 5.3, allowing runtime resolution of static method calls for more flexible inheritance.Main issue: LSB vs. traditional polymorphism; LSB's practical applications and potential perfo

JWT is an open standard based on JSON, used to securely transmit information between parties, mainly for identity authentication and information exchange. 1. JWT consists of three parts: Header, Payload and Signature. 2. The working principle of JWT includes three steps: generating JWT, verifying JWT and parsing Payload. 3. When using JWT for authentication in PHP, JWT can be generated and verified, and user role and permission information can be included in advanced usage. 4. Common errors include signature verification failure, token expiration, and payload oversized. Debugging skills include using debugging tools and logging. 5. Performance optimization and best practices include using appropriate signature algorithms, setting validity periods reasonably,

Article discusses essential security features in frameworks to protect against vulnerabilities, including input validation, authentication, and regular updates.

Sending JSON data using PHP's cURL library In PHP development, it is often necessary to interact with external APIs. One of the common ways is to use cURL library to send POST�...

The article discusses adding custom functionality to frameworks, focusing on understanding architecture, identifying extension points, and best practices for integration and debugging.

An official introduction to the non-blocking feature of ReactPHP in-depth interpretation of ReactPHP's non-blocking feature has aroused many developers' questions: "ReactPHPisnon-blockingbydefault...
