Part 3: Testing Model Relationships in Laravel — POLYMORPHIC

Tony Ayeni
4 min readSep 29, 2019

This part deals with more complex model relationships, the polymorphic type. Just like before, we shall stick to the official documentation examples and work out befitting tests. To have a low down on the earlier parts see link directly below 👇

POLYMORPHIC RELATIONSHIPS

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

1. One-to-One Polymorphic Relationship

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.

Scenario:

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

morphTo()

— An image table can be morphed into any model (i.e serve many different models), eg User or Post model in our case.

// App/Image.php
...
public function imageable()
{
return $this->morphTo();
}
morphTo() Polymorphic Relationship Test — tests/Unit/ImagesTest.php

morphOne()

  • — A user/post model can morph one instance of the same image model table (i.e make use of same image table but one record per user/post).
  • — INVERSE of morphTo relationship.
// App/User.php
...
public function image()
{
return $this->morphOne(Image::class, 'imageable');
}
morphOne() Relationship Test — tests/Unit/UserTest.php (same test for Post model).

Files relevant to this test 👇

- MODEL FILES (App/)
-- User.php
-- Post.php
-- Image.php
- MIGRATION FILES (database/migrations/)
-- 2014_10_12_000000_create_users_table.php
-- 2019_09_27_100604_create_posts_table.php
-- 2019_09_28_143143_create_images_table.php
- MODEL FACTORY FILES (database/factories/)
-- UserFactory.php
-- PostFactory.php
-- ImageFactory.php

- UNIT TEST FILES (tests/Units/)
-- UserTest.php
-- PostsTest.php
-- ImagesTest.php

2. One-to-Many (Polymorphic)

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. — Laravel.com

Scenario:

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.

morphTo()

— A comment table can be morphed to any model (i.e serve many different models), eg Video or Post model in our case.

// App/Comment.php
...
public function commentable()
{
return $this->morphTo();
}
morphTo() Many-to-Many Polymorphic Relationship Test — tests/Unit/CommentsTest.php

morphMany()

  • — A video/post model can morph many instances of the same comment model table (i.e make use of same comment table for many records).
  • — INVERSE of morphTo relationship.
// App/Video.php
...
public function comments()
{
return $this->morphMany(Comment::class, 'commentable');
}
morphMany() Polymorphic Relationship Test — tests/Unit/VideosTest.php (same test for Post model).

Files relevant to this test 👇

- MODEL FILES (App/)
-- User.php
-- Post.php
-- Video.php
-- Comment.php
- MIGRATION FILES (database/migrations/)
-- 2019_09_28_211240_create_videos_table.php
-- 2019_09_28_221627_add_commentable_id_and_commentable_type_to_comments_table.php
- MODEL FACTORY FILES (database/factories/)
-- VideoFactory.php

- UNIT TEST FILES (tests/Unit/)
-- PostsTest.php
-- VideosTest.php
-- CommentsTest.php

3. Many-to-Many (Polymorphic)

Many-to-Many Polymorphic Relationship: We shall stick to the examples used in the official Laravel documentation.

morphToMany() & morphedByMany

Scenario:

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.

A particular tag may belong to a post and yet belong to a video (two different models). Tags are like subtle categorizations that help site users get a first time tip into what a given page content is about and also link related resources to one another. The concerned models in this test are Posts, Videos, Tags and Taggables.

morphToMany()

A tag can be morphed to many models i.e serve many different models but Video or Post model in this example. A video/post model can belongsToMany instances of different tags from the tag model table. However, we are dealing with a polymorphic model thus morphToMany. Similarly, each tag also can be assigned to multiple instances of different models such as video, posts and even more others.

— The INVERSE relationship of morphedByMany

// App/Video.php
...
public function tags()
{
return $this->morphToMany(Tag::class, 'taggable');
}

// App/Post.php
...
public function tags()
{
return $this->morphToMany(Tag::class, 'taggable');
}
morphToMany() Many-to-Many Polymorphic Relationship Test — tests/Unit/VideosTest.php (exactly same for PostsTest.php).

morphedByMany()

On the tagtag model, a particular tag entry can be assigned to (morphed by) many different model instances of video or post models.

— INVERSE of morphToMany relationship.

// App/Tag.php<?phpnamespace App;use Illuminate\Database\Eloquent\Model;class Tag extends Model
{
protected $fillable = [
'name', 'description'
];
public function posts()
{
return $this->morphedByMany(Post::class, 'taggable');
}
public function videos()
{
return $this->morphedByMany(Video::class, 'taggable');
}
}

// App/Taggable.php

<?php
namespace App;
use Illuminate\Database\Eloquent\Model;class Taggable extends Model
{
protected $fillable = [
'tag_id', 'taggable_id', 'taggable_type'
];
}
morphedByMany() Many-to-Many Polymorphic Relationship Test — tests/Unit/TaggablesTest.php.

NB: To fix PHP 7.4 compatibility issues with Faker factory package simply update: $ composer update fzaninotto/faker

Files relevant to this test 👇

- MODEL FILES (App/)
-- Video.php
-- Post.php
-- Tag.php
-- Taggable.php
- MIGRATION FILES (database/migrations/)
-- ...others have been created in previous tests
-- 2019_09_29_000212_create_taggables_table.php
- MODEL FACTORY FILES (database/factories/)
-- TaggableFactory.php

- UNIT TEST FILES (tests/Unit/)
-- VideosTest.php
-- PostsTest.php
-- TagsTest.php

--

--