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

--

--

Tony Ayeni
Tony Ayeni

Responses (1)