Part 3: Testing Model Relationships in Laravel — POLYMORPHIC
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 👇
- Part 1: Intro and Schema Tests
- Part 2: Testing Basic Model Relationships
- Part 3: Testing Polymorphic Relationships ⏯
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();
}
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');
}
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();
}
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');
}
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');
}
morphedByMany()
On the tag
tag 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'
];
}
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
- 👉Part 1: Intro and Schema Tests
- 👉Part 2: Testing Basic Model Relationships
- ⏯Part 3: Testing Polymorphic Relationships