Laravel Cloud is here! Zero-config managed infrastructure for Laravel apps. Deploy now.

Enforcing Morph Maps in Laravel

Published on by

Enforcing Morph Maps in Laravel image

Oliver Nybroe contributed a pull request to Laravel (8.59.0) that allows developers to require that morph maps to be set, rather than defaulting to fully qualified class names.

By using the enforceMorphMap method instead of the traditional morphMap method, Laravel will ensure that all morphs are mapped to an alias and throw a ClassMorphViolationException exception if one is not mapped.

// If any other models are morphed but not mapped, Laravel
// will throw a `ClassMorphViolationException` exception.
Relation::enforceMorphMap([
'user' => User::class,
]);

You can enforce a morph map using the single call shown above, or you can use the standalone requireMorphMap method on the Relation class:

// Turn morph map enforcement on (new in 8.59.0).
Relation::requireMorphMap();
 
// And then map your morphs in the standard way.
Relation::morphMap([
'user' => User::class,
]);

Morph Map Background

When creating polymorphic relationships in Laravel, the default behavior has always been to store the class name of the related model in the database. From the Laravel documentation:

By default, Laravel will use the fully qualified class name to store the "type" of the related model. For instance, given the one-to-many relationship example above where a Comment model may belong to a Post or a Video model, the default commentable_type would be either App\Models\Post or App\Models\Video, respectively.

Using this default method means that your database will end up populated with the class names of your models, which tightly couples the data in your database to the names of your classes.

Laravel has always given us the ability to decouple the class name from the database by registering a MorphMap which provides an alias for a class, breaking that association:

// Store `user` in the database, instead of `App\User`
Relation::morphMap([
'user' => User::class,
]);

While this behavior has been available for some time, it has never been possible to strictly require it.

Benefits of Morph Maps

The benefit of having a morph map in the first place is to decouple your application logic from your stored data. Storing class names in your database can lead to pernicious, hard to debug errors.

If you change the name of one of the classes that has been morphed, all of the references in your database will no longer line up with your application. This sneaky part about this is that it's likely that nothing will fail in development or testing! Unless your test suite somehow populates your database with the previous class name, all of your tests will be green. Only when you deploy to production will the class and data mismatch arise.

The scenario above could be solved by writing a migration to update your stored data, but there's no guarantee that you (or the next person, or the next) will remember that it's necessary.

Rather than relying on your company's institutional memory, morph maps break the association and free you from that potential bug.

Benefits of Enforcing A Morph Map

In the same way that morph maps free you from having to remember to update stored data when you refactor a class, requiring a morph map frees you from having to remember that you need to register a class in the first place.

Before morph maps were enforceable, it was incumbent on the developer or the team to remember to map morphs in the first place.

For example, one developer could set up a morph map like this:

Relation::morphMap([
'image' => Image::class,
'post' => Post::class,
]);

and then another developer could come along and start using App\Video as a morphed relation without adding it to the map!

Now, in the database, you'd see the following types:

  • image - from the morph map
  • post - from the morph map
  • App\Video - from the Laravel default implementation

Because the developer didn't know there was a morph map set up, they didn't register their new Video model. Given an old enough application or a large enough team, this is an inevitability.

Now by enforcing a morph map, the App\Video cannot be used in a morph unless an alias has been set up. A ClassMorphViolationException will be thrown.

// All future morphs *must* be mapped!
Relation::enforceMorphMap([
'image' => Image::class,
'post' => Post::class,
]);

This new feature allows you take what was implicit knowledge and make it an explicit code requirement.

The more explicit we can make the requirements of an application the easier it will be to maintain over time, and the easier it will be to bring on new developers.

Aaron Francis photo

Writing and tweeting about building products + Laravel.

Working on torchlight.dev & hammerstone.dev.

Dad to boy/girl twins.

Cube

Laravel Newsletter

Join 40k+ other developers and never miss out on new tips, tutorials, and more.

image
Tinkerwell

Enjoy coding and debugging in an editor designed for fast feedback and quick iterations. It's like a shell for your application – but with multi-line editing, code completion, and more.

Visit Tinkerwell
Curotec logo

Curotec

World class Laravel experts with GenAI dev skills. LATAM-based, embedded engineers that ship fast, communicate clearly, and elevate your product. No bloat, no BS.

Curotec
Bacancy logo

Bacancy

Supercharge your project with a seasoned Laravel developer with 4-6 years of experience for just $3200/month. Get 160 hours of dedicated expertise & a risk-free 15-day trial. Schedule a call now!

Bacancy
Tinkerwell logo

Tinkerwell

The must-have code runner for Laravel developers. Tinker with AI, autocompletion and instant feedback on local and production environments.

Tinkerwell
Get expert guidance in a few days with a Laravel code review logo

Get expert guidance in a few days with a Laravel code review

Expert code review! Get clear, practical feedback from two Laravel devs with 10+ years of experience helping teams build better apps.

Get expert guidance in a few days with a Laravel code review
PhpStorm logo

PhpStorm

The go-to PHP IDE with extensive out-of-the-box support for Laravel and its ecosystem.

PhpStorm
Laravel Cloud logo

Laravel Cloud

Easily create and manage your servers and deploy your Laravel applications in seconds.

Laravel Cloud
Kirschbaum logo

Kirschbaum

Providing innovation and stability to ensure your web application succeeds.

Kirschbaum
Shift logo

Shift

Running an old Laravel version? Instant, automated Laravel upgrades and code modernization to keep your applications fresh.

Shift
Harpoon: Next generation time tracking and invoicing logo

Harpoon: Next generation time tracking and invoicing

The next generation time-tracking and billing software that helps your agency plan and forecast a profitable future.

Harpoon: Next generation time tracking and invoicing
Lucky Media logo

Lucky Media

Get Lucky Now - the ideal choice for Laravel Development, with over a decade of experience!

Lucky Media
SaaSykit: Laravel SaaS Starter Kit logo

SaaSykit: Laravel SaaS Starter Kit

SaaSykit is a Multi-tenant Laravel SaaS Starter Kit that comes with all features required to run a modern SaaS. Payments, Beautiful Checkout, Admin Panel, User dashboard, Auth, Ready Components, Stats, Blog, Docs and more.

SaaSykit: Laravel SaaS Starter Kit

The latest

View all →
Clawdbot Rebrands to Moltbot After Trademark Request From Anthropic image

Clawdbot Rebrands to Moltbot After Trademark Request From Anthropic

Read article
Automate Laravel Herd Worktrees with This Claude Code Skill image

Automate Laravel Herd Worktrees with This Claude Code Skill

Read article
Laravel Boost v2.0 Released with Skills Support image

Laravel Boost v2.0 Released with Skills Support

Read article
Laravel Debugbar v4.0.0 is released image

Laravel Debugbar v4.0.0 is released

Read article
Radiance: Generate Deterministic Mesh Gradient Avatars in PHP image

Radiance: Generate Deterministic Mesh Gradient Avatars in PHP

Read article
Speeding Up Laravel News With Cloudflare image

Speeding Up Laravel News With Cloudflare

Read article