Polymorphic relationships in Laravel and their use cases

Rishni Meemeduma
4 min readNov 18, 2024

--

In software development, it’s common to encounter models that belong to multiple entities. A great example is comments. Take an application like Facebook, for instance: comments can be associated with posts, stories, or videos while maintaining the same structure. This behavior is an excellent illustration of polymorphism in action.

In this article, we will explore polymorphic relationships in Laravel, delve into how they work, and discuss the various use cases where they shine the most.

Polymorphic Relationships

A polymorphic relationship lets one model be connected to multiple other models using a single setup. For example, if you’re building an app where users can share blog posts and videos, a Comment model can be linked to both Post and Video models. This means users can comment on either posts or videos without needing separate comment systems.

One to One (Polymorphic)

A one-to-one polymorphic relationship is like a regular one-to-one relationship, but with a twist: the child model can be linked to more than one type of parent model using just one connection. For example, a Profile could belong to either a User or an Admin, all through the same relationship setup.

let’s examine the table structure:

admin
id - integer
name - string

users
id - integer
name - string

profile
id - integer
avatar- string
profileable_id - integer
profileable_type - string

The profileable_id column will contain the id values of admin or users. While the profileable_type column will contain the class name of the parent users , admin models. it’s either App\Models\Users or App\Models\Admin

Now, let check the models

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\MorphTo;

class Profile extends Model
{
/**
* Get the parent profileable model (user or admin).
*/
public function profileable(): MorphTo
{
return $this->morphTo();
}
}

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\MorphOne;

class Users extends Model
{
/**
* Get the users profile.
*/
public function profile(): MorphOne
{
return $this->morphOne(Profile::class, 'profileable');
}
}

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\MorphOne;

class Admin extends Model
{
/**
* Get the admin profile.
*/
public function profile(): MorphOne
{
return $this->morphOne(Profile::class, 'profileable');
}
}

One to Many (Polymorphic)

A one-to-many polymorphic relationship is like a regular one-to-many relationship, but with a difference: the child model can be linked to multiple types of parent models using one setup. For example, a Comment model can belong to either a Post or a Video, allowing users to leave comments on both without needing separate comment systems

let’s examine the table structure required to build this relationship:

posts
id - integer
title - string
body - text

videos
id - integer
title - string
url - string

comments
id - integer
body - text
commentable_id - integer
commentable_type - string

Next, Model structure

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\MorphTo;

class Comments extends Model
{
/**
* Get the parent commentable model (posts or vidoes).
*/
public function commentable(): MorphTo
{
return $this->morphTo();
}
}

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\MorphOne;

class Videos extends Model
{
/**
* Get the video comments.
*/
public function comments(): MorphMany
{
return $this->morphMany(Comments::class, 'commentable');
}
}

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\MorphOne;

class Posts extends Model
{
/**
* Get the post comments.
*/
public function comments(): MorphMany
{
return $this->morphMany(Comments::class, 'commentable');
}
}

Many to Many (Polymorphic)

Many-to-many polymorphic relationships let multiple models share a common relationship with another model. For example, both a Post and a Video can have tags, and those tags are stored in a single table. This setup allows you to manage unique tags that can be linked to either posts or videos, keeping things organized and flexible.

let’s examine the table structure required to build this relationship:


posts
id - integer
name - string

videos
id - integer
name - string

tags
id - integer
name - string

taggables
tag_id - integer
taggable_id - integer
taggable_type - string

Now, let check models

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\MorphToMany;

class Post extends Model
{
/**
* Get all of the tags for the post.
*/
public function tags(): MorphToMany
{
return $this->morphToMany(Tag::class, 'taggable');
}
}

class Tag extends Model
{
/**
* Get all of the posts that are assigned this tag.
*/
public function posts(): MorphToMany
{
return $this->morphedByMany(Post::class, 'taggable');
}

/**
* Get all of the videos that are assigned this tag.
*/
public function videos(): MorphToMany
{
return $this->morphedByMany(Video::class, 'taggable');
}
}

Custom Polymorphic Types

By default, Laravel saves the full class name of the related model (like App\Models\Post or App\Models\Video) in the commentable_type column to identify the type of model a comment belongs to.

However, if you want to simplify this and avoid saving full class names in the database — for example, just save post or video instead—you can customize this behavior. This makes the database cleaner and less tied to the internal structure of your application.

You can do this by defining a morph map, which tells Laravel to use shorter, custom names instead of full class names.

use Illuminate\Database\Eloquent\Relations\Relation;

class AppServiceProvider extends ServiceProvider
{
public function boot()
{
Relation::enforceMorphMap([
'post' => App\Models\Post::class,
'video' => App\Models\Video::class,
]);
}
}

In this example:

  • The post key maps to the App\Models\Post class.
  • The video key maps to the App\Models\Video class.

This will ensure that the commentable_type column in your database stores post or video instead of the fully qualified class names.

common usage,

use App\Models\Post;
use App\Models\Video;
use App\Models\Comment;

// Attach a comment to a post
$post = Post::find(1);
$post->comments()->create([
'body' => 'This is a comment on a post',
]);

// Attach a comment to a video
$video = Video::find(1);
$video->comments()->create([
'body' => 'This is a comment on a video',
]);

// fetching comments
// Get all comments for a post
$postComments = Post::find(1)->comments;

// Get all comments for a video
$videoComments = Video::find(1)->comments;

// accessing the parents model
$comment = Comment::find(1);
$parent = $comment->commentable; // Returns the related Post or Video model

Thank you !

--

--

Rishni Meemeduma
Rishni Meemeduma

No responses yet