Re-Introducing Eloquent’s Polymorphic Relationships
You've probably used different types of relationships between models or database tables, like those commonly seen in Laravel: one-to-one, one-to-many, many-to-many, and has-many-through. But there's another type of relationship that's not so common: polymorphic. So what is a polymorphic relationship?
A polymorphic relationship is where a model can belong to more than one other model on a single association.
To clarify this, let's create an imaginary situation where we have a Topic
and a Post
model. Users can leave comments on both topics and posts. Using polymorphic relationships, we can use a single comments
table for both of these scenarios. Surprising, yeah? This seems a bit impractical since, ideally, we'd have to create a post_comments
table and a topic_comments
table to differentiate the comments. With polymorphic relationships, we don't need two tables. Let's look into polymorphic relationships through a practical example.
What We'll Be Building
We'll be creating a demo music app which has songs and albums. In this app, we'll have the option to upvote both songs and albums. Using polymorphic relationships, we'll use a single upvotes table for both of these scenarios. First, let's examine the table structure required to build this relationship:
albums
id - integer
name - string
songs
id - integer
title - string
album_id - integer
upvotes
id - integer
upvoteable_id - integer
upvoteable_type - string
Let's talk about the upvoteable_id
and upvoteable_type
columns which may seem a bit foreign to those who've 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 of the owning model. The upvoteable_type
column is how the ORM determines which "type" of owning model to return when accessing the upvoteable
relation.
Generating the Models Alongside Migrations
I am assuming you already have a Laravel app that's up and running. If not, this premium quick start course might help. Let's start by creating the three models and migrations, then edit the migrations to suit our needs.
php artisan make:model Album -m
php artisan make:model Song -m
php artisan make:model Upvote -m
Note, passing the -m
flag when creating models will generate migrations associated with those models as well. Let's tweak the up
method in these migrations to get the desired table structure:
{some_timestamp}_create_albums_table.php
public function up()
{
Schema::create('albums', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->timestamps();
});
}
{some_timestamp}_create_songs_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('album')->onDelete('cascade');
});
}
{some_timestamp}_create_upvotes_table.php
public function up()
{
Schema::create('upvotes', function (Blueprint $table) {
$table->increments('id');
$table->morphs('upvoteable'); // Adds unsigned INTEGER upvoteable_id and STRING upvoteable_type
$table->timestamps();
});
}
We can now run the artisan migrate
command to create the three tables:
php artisan migrate
Continue reading %Re-Introducing Eloquent’s Polymorphic Relationships%