Subscribe to PHP Freaks RSS

Re-Introducing Eloquent’s Polymorphic Relationships

syndicated from planet-php.net on July 14, 2017

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%