> ## Content Index
> Fetch the complete content index at: https://madewithlove.com/blog/llms.txt
> Use this file to discover other available public pages before exploring further.

# Laravel's Polymorphic relationships and Polymorphism
- URL: https://madewithlove.com/blog/laravels-polymorphic-relationships-and-polymorphism/
- Published: 2021-12-01T09:41:00.000Z
- Updated: 2024-06-06T14:15:17.000Z
- Author: Tony Messias
- Tags: Engineering, Laravel

As many of you know Laravel provides a concept for dealing with Polymorphism in Eloquent relationships called “[Polymorphic Relationships](https://laravel.com/docs/5.8/eloquent-relationships#polymorphic-relationships)“. It’s a nice abstraction, but if you are not careful it might lead to a lot of type checking at runtime all over the place.

There’s nothing wrong with the Polymorphic Relationships, you only have to keep in mind that whenever you need it, you have to think about behavior instead of types. Luckily for us, Object-Oriented Programming (OOP) has a concept that fits like a glove here: Polymorphism.

The naming isn’t just an accident. Let’s see the problem before we talk about the solution.

## The Problem

Let’s assume we are building a really robust and abstract permission system. The idea is that we can create permissions so that users can interact with certain resources. A permission entry has a subject (the resource) and a consumer (the user).

```
users
  id - int
  name - string

resources
  id - int
  name - string
  external_id - string # represents the resource ID in an external service

permissions
  id - int
  subject_type - string
  subject_id - int
  consumer_type - string
  consumer_id - int
```

Good enough. If you pay attention to the *permissions* table, you will see that both *consumer* and *subject* concepts got 2 fields for the relationship: *type* and *id*.

The *type* one holds the reference to the Eloquent model class\*, while the *id* one holds the primary key of that model.

*\* Note: by defailt, the Fully-Qualified Class Name* *(FQCN) will be stored in the database.* *That is sometimes a pain when you have to rename models or move classes around* when *refactoring. However, you can* *fully configured what is stored as the type using aliases that map to the actual FQCN, see [here](https://laravel.com/docs/5.8/eloquent-relationships#custom-polymorphic-types)*.

Your models should look something like this:

```php
<?php

class User extends Model
{
  public function permissions()
  {
    return $this->morphMany(Permission::class, 'consumer');
  }
}

class Resource extends Model
{
  public function permissions()
  {
    return $this->morphMany(Permission::class, 'subject');
  }
}

class Permission extends Model
{
  public function subject()
  {
    return $this->morphTo();
  }

  public function consumer()
  {
    return $this->morphTo();
  }
}
```

Cool. Here’s an example of these relationships in use:

```php
$user = User::all();

foreach ($user->permissions as $permission) {
  // Should print out the resource 
  // associated with this permission.
  dump($permission->subject);
}
```

All fine so far. Now, let’s say that we need to export these permissions to an external service. Ok, simple enough, something like this should do the trick:

```php
$cursor = Permission::query()->cursor();

foreach ($cursor as $permission) {
   Zttp::post($this->url(), [
     'resource_id' => $permission->subject->external_id,
     'user_id' => $permission->consumer->id,
   ]);
}
```

Uhm. At first sight, it might look fine. The ***$permission->subject*** returns the *Resource* associated with the *Permission*, while the ***$permission->consumer*** is the *User* that was granted that permission, right? Well, not so sure. Remember that we are using a polymorphic relationship here.

In other words, it *might* be that these are indeed the behavior of the permission, but it also might be something else completely. That’s why we are using a polymorphic relationship in the first place.

Let’s make it a bit more complex by introducing *grouping*.

## Grouping Resources and Users

Now, we can group both *Resources* and *Users*. And you can grant these *Permissions* to them using these groups. However, the external service doesn’t have any of these “grouping” concepts. It only cares about *Users* and *Resources*, a flat map.

Since our *Permission* model is using *polymorphic relationships*, we can easily make a *UserGroup* a consumer and a *ResourceGroup* a subject.

Luckily for us, our external service accepts a list of IDs for both resources and users.

Our first code to export these permissions need some change, because now the ***$permission->subject*** and **$permission->consumer** no longer return only *User* and *Resource* instances, respectively. It can also return *UserGroup* and/or *ResourceGroup*, again, respectively.

Easy enough, you can do something like this:

```php
$cursor = Permission::query()->cursor();

foreach ($cursor as $permission) {
   Zttp::post($this->url(), [
     'resource_id' => $permission->subject instanceof Resource 
       ? [$permission->subject->external_id] 
       : $permission->subject->resources()->pluck('external_id')->all(),
     'user_id' => $permission->consumer instanceof User
       ? [$permission->consumer->id]
       : $permission->consumer->users()->pluck('id')->all(),
   ]);
}
```

Uhm… it doesn’t look pretty, does it? Also, what happens if we add yet another model as *Subject*? Or another *Consumer*? Yeah, more type checks, not cool. You can split these up into smaller methods, but that’s just hiding complexity.

There’s another way.

## Enters Polymorphism

As I mentioned in the beginning of the post, you can use polymorphism to solve this.

> *In programming languages and type theory, polymorphism is the provision of a single interface to entities of different types or the use of a single symbol to represent multiple different types.*
> 
> [W](https://en.wikipedia.org/wiki/Polymorphism%5F%28computer%5Fscience%29)i[kipedia](https://en.wikipedia.org/wiki/Polymorphism%5F%28computer%5Fscience%29)

Reading the definition, that looks exactly like what we want. In fact, there was a hint in the relationship name: “Polymorphic Relationship”.

I’m not saying this is the only way of dealing with this. However, I found this approach really good and clean.

**Whenever you need a polymorphic relationship, introduce an interface that will represent that behavior.**

That’s it. In our case, we have two concepts: *Subject* and *Consumer* of permissions. All we care right now is a way to retrieve the IDs of these types. The interfaces would look like this:

```php
namespace App\Permissions;

interface Subject
{
  public function getSubjectIdsForPermissions(): array;
}

interface Consumer
{
  public function getConsumerIdsForPermissions(): array;
}
```

Cool. We these in the same file here, but that’s just for demonstration purposes. Now, let’s implement these in our *User* and *UserGroup* models, which are *consumer*.

```php
use App\Permissions\Consumer as PermissionsConsumer;

class User extends Model implements PermissionsConsumer
{
  public function getConsumerIdsForPermissions(): array
  {
    return [$this->id];
  }
}

class UserGroup extends Model implements PermissionsConsumer
{
  public function users()
  {
    return $this->belongsToMany(User::class);
  }

  public function getConsumerIdsForPermissions(): array
  {
    return $this->users()->pluck('id')->all();
  }
}
```

Looks good. Now, the *Resource* and *ResourceGroup* models are *subjects* of our permissions, so they could look like this:

```php
use App\Permissions\Subject as PermissionsSubject;

class Resource extends Model implements PermissionsSubject
{
  public function getSubjectIdsForPermissions(): array
  {
    return [$this->external_id];
  }
}

class ResourceGroup extends Model implements PermissionsSubject
{
  public function resources()
  {
    return $this->belongsToMany(Resource::class);
  }

  public function getSubjectIdsForPermissions(): array
  {
    return $this->resources()->pluck('external_id')->all();
  }
}

```

Great. Now, our final implementation of the exporting would look like this:

```php
$cursor = Permission::query()->cursor();

foreach ($cursor as $permission) {
   Zttp::post($this->url(), [
     'resource_id' => $permission->subject->getSubjectIdsForPermissions(),
     'user_id' => $permission->consumer->getConsumerIdsForPermissions(),
   ]);
}
```

That’s better, right? At least to me. You can even help your IDE with auto-completions on these relationships by adding docblocks in your *Permission* model, like:

```php
/**
 * Attributes
 * 
 * @property \App\Permissions\Subject $subject
 * @property \App\Permissions\Consumer $consumer
 */
class Permission extends Model
{
  public function subject()
  {
    return $this->morphTo();
  }

  public function consumer()
  {
    return $this->morphTo();
  }
}
```

Now, whenever you reach for **$permission->subject** or **$permission->consumer**, your IDE will show the available methods in those interfaces.

You can go even further and use ***@property-read*** instead, which will remind you in your IDE that you cannot override these properties externally. Then, you can have domain methods that are correctly type-hinted using the interfaces, like so:

```php
use App\Permissions\Subject as PermissionsSubject;
use App\Permissions\Consumer as PermissionsConsumer;

/**
 * @property-read \App\Permissions\Subject $subject
 * @property-read \App\Permissions\Consumer $consumer
 */
class Permission extends Model
{
  public static function newPermission(
    PermissionsSubject $subject,
    PermissionsConsumer $consumer
  ): Permission {
    $permission = new static();

    $permission->subject()->associate($subject);
    $permission->consumer()->associate($consumer);

    return $permission;
  }

  public function subject()
  {
    return $this->morphTo();
  }

  public function consumer()
  {
    return $this->morphTo();
  }
}
```

## Conclusion

Although these examples were pretty simple (we are only using *get* operations in our interfaces), you can have more complex abstractions and do more fancy things with these interfaces. Each class that implements the interface will have its own behavior for these.

Anyways, hope this was fun to you. I have been thinking about this approach for a while over the past few days, because we are working with a couple Polymorphic relationships in one of our projects.

Let me know what you think.

## Stay in the loop

Weekly updates for SaaS founders, developers and investors. 

Subscribe 

Email sent! Check your inbox to complete your signup. 

No spam. Unsubscribe anytime.