Polymorphism Concept with Laravel

manish maharjanmanish maharjan
6 min read

In the software development world, polymorphism is the concept that plays a vital role to ease the development if conduct with appropriate judiciary. The concept allow the to treated varieties of object types to be treated as objects sharing a common types, fundamentally aiding in object oriented feature to promotes flexibility and extensibility in a codebase. As Laravel being one of the popular framework following OOP, can leverage polymorphism extensively to deliver applications in more robust way and maintainable state. Through this blog, we will gain knowledge of the polymorphism concept with Laravel with exploration of some real world examples to implement in out application.

Polymorphism

Polymorphism, is derived form Greek that means "many forms". This is the ability to represent entitles of different types qith a single interface.

With Laravel, it is often used in database relationships, facilitating a model to belong to multiple other models, without requiring specification of any types. The polymorphism property can be achieved in Laravel , with its powerful Eloquent ORM and its supports for morph relationships.

Polymorphic Relationships in Laravel

Types of polymorphic relationship supported in Laravel:

One-to-one

A one-to-one polymorphic relation is similar to a typical one-to one relation, however, a child model is connected to multiple models based on single association. Consider a scenario where both a blog Post and a User can have a polymorphic relationship with an Document model. Employing a one-to-one polymorphic relationship enables the usage of a common table containing distinct documents that can be linked to both posts and users. Now, let's delve into the table structure:

Model Structure

<?php
namespace App\Models;

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

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

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

class PostModel extends Model
{
    /**
     * Get the attachment for post
     */
    public function attachment(): MorphOne
    {
        return $this->morphOne(AttachmentModel::class, 'attachment');
    }
}

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

class UserModel extends Model
{ 
    /**
     * Get the attachment for user
     */
    public function attachment(): MorphOne
    {
        return $this->morphOne(AttachmentModel::class, 'attachment');
    }
}

Creating and Retrieving Polymorphic Relationships:

use App\Models\Post;

// Add a attachment for a post
$post = PostModel::find(1);
$attachement = $post->attachment()->create([
    'url' => 'https://www.google.com/imgurl.png'
]);

// Get attachment for a post
$attachements = $post->attachement;
echo $post->owner_id // output: 1
echo $post->owner_type // output: App\Models\PostModel

One-to-many

A one-to-many polymorphic relation is similar to a typical one-to many relation, however, a child model is connected to multiple types of model based on single association.

We can refer to the table structure same as one-to-one polymorphic relationship.

And for model structure just replace morphOne with morphMany.


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

class PostModel extends Model
{
    /**
     * Get the attachments for post
     */
    public function attachments(): MorphMany
    {
        return $this->morphMany(AttachmentModel::class, 'attachment');
    }
}

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

class UserModel extends Model
{ 
    /**
     * Get the attachments for user
     */
    public function attachments(): MorphMany
    {
        return $this->morphMany(AttachmentModel::class, 'attachment');
    }
}

Relationship retrieval

use App\Models\Post;

// Get attachment for a post
$attachements = $post->attachements;
$attchments->forEach(function($attachment){
    // ...
})

One of many

One of many relationship is sometimes useful when you a model have many related models, yet you only needs only one record(latest or oldest) out of many related records.

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

class PostModel extends Model
{
    /**
     * Get latest attachement related to post.
     */
    public function latestAttachments(): MorphOne
    {
        return $this->morphOne(AttachmentModel::class, 'attachment')->latestOfMany();
    }
}

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

class UserModel extends Model
{ 
    /**
     * Get latest attachement related to post.
     */
    public function attachment(): MorphOne
    {
        return $this->morphOne(AttachmentModel::class, 'attachment')->oldestOfMany();
    }
}

Many to Many

Many to many polymorphic relationship is a bit complicated than one-to-one and one-to-many polymorphic relationships. it allows for versatile associations between multiple models. let's consider an example scenario that includes models for User, Post and Tag. Both users and posts can be tagged with various tags, inversely tags be associated with both users and posts.

Table Structure

use Illuminate\Database\Eloquent\Relations\MorphToMany;
class PostModel extends Model {
    public function tags(): MorphToMany
    {
        return $this->morphToMany(TagModel::class, 'taggable');
    }
}

class UserModel extends Model {
    public function tags() : MorphToMany
    {
        return $this->morphToMany(Tag::class, 'taggable');
    }
}

class Tag extends Model {
    public function posts() : MorphToMany
    {
        return $this->morphedByMany(PostModel::class, 'taggable');
    }

    public function users() : MorphToMany
    {
        return $this->morphedByMany(UserModel::class, 'taggable');
    }
}

Using the relationship

/*
* Attaching tags to post and user
*/
$post = PostModel::find(1);
// Attaching tags with IDs 1, 2, 3 to the post
$post->tags()->attach([1, 2, 3]); 

$user = UserModel::find(1);
// Attaching tags with IDs 2, 3, 4 to the user
$user->tags()->attach([2, 3, 4]);
/*
* Retrieving and detaching tags related to post post and user
*/
$post = PostModel::find(1);
$postTags = $post->tags; //gets tags for post
$post->tags()->detach([1, 2]); // remove tags 1, 2 for post

$user = UserModel::find(1);
$userTags = $user->tags; //gets tags for user

$post->tags()->detach([3, 4]); // remove tags 1, 2 for user

Pros

  • Polymorphic relations facilitates to build flexible, dynamic data structures that accomodate with different type of relationships, mitigating complex schema changes.

  • Code can be made more cleaner and maintainable with the abstraction of relationships logic into polymorphic methods.

  • Code becomes more reusable as the same relation can be shared across multiple models.

Cons

  • If not pre-planned the motive for polymorphic relationships enrolment, specially, when dealing with complex data structures, might add up complexity to codebase. Understanding and managing such relationship might need more efforts from developers.

  • Polymorphic relationships often involve extra queries to resolve queries dynamically, which can cause overhead in performance for large datasets and complex relationships. Delicate optimisation might help to maintain optimal performance level.

  • Developers might get confused to understand the relationships between different models leading to potential confusion and errors. Proper documentation might help one to minimise the ambiguity in future.

  • Sometime bypassing the integrity constraints like foreign key constraints might create challenge to enforce rules for data integrity at database level, increasing the risk for data inconsistency

References

Conclusion

Wrapping up, Polymorphism is a powerful concept that enables you to build scalable and maintainable applications. By leveraging polymorphic relationships, you can design databases that are flexible and adaptable to dynamic requirements. Understanding and implementing polymorphism in Laravel opens up the possibilities for creating elegant and efficient solutions to complex problems. Though sometimes concept might create complexity, impact on performance, ambiguity possibilities and bypass data integrity constraints challenges, it can be sorted out with careful consideration to address the problems proactively.

I believe this blog helps you to understand the concept and usage of polymorphism in Laravel better. The aims is to make it easier to grasp and prevent confusion in a codebase.

Your thoughts are welcome ๐Ÿ˜Š and I'm eager to create more helpful content like this in the future.

14
Subscribe to my newsletter

Read articles from manish maharjan directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

manish maharjan
manish maharjan