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
Paragraph

Manage your Laravel app as if it was a CMS – edit any text on any page or in any email without touching Blade or language files.

Visit Paragraph
Laravel Forge logo

Laravel Forge

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

Laravel Forge
Tinkerwell logo

Tinkerwell

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

Tinkerwell
No Compromises logo

No Compromises

Joel and Aaron, the two seasoned devs from the No Compromises podcast, are now available to hire for your Laravel project. ⬧ Flat rate of $7500/mo. ⬧ No lengthy sales process. ⬧ No contracts. ⬧ 100% money back guarantee.

No Compromises
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
LoadForge logo

LoadForge

Easy, affordable load testing and stress tests for websites, APIs and databases.

LoadForge
Paragraph logo

Paragraph

Manage your Laravel app as if it was a CMS – edit any text on any page or in any email without touching Blade or language files.

Paragraph
Lucky Media logo

Lucky Media

Bespoke software solutions built for your business. We ♥ Laravel

Lucky Media
Lunar: Laravel E-Commerce logo

Lunar: Laravel E-Commerce

E-Commerce for Laravel. An open-source package that brings the power of modern headless e-commerce functionality to Laravel.

Lunar: Laravel E-Commerce
Bacancy - Staff Augmentation logo

Bacancy - Staff Augmentation

Leave your web app development hustles to the leading IT Staff Augmentation Service Providers. Choose from an extensive pool of 1050+ developers and give yourself the sigh of success you deserve with Bacancy. Get In Touch Today!

Bacancy - Staff Augmentation
DocuWriter.ai logo

DocuWriter.ai

Save hours of manually writing Code Documentation, Comments & DocBlocks, Test suites and Refactoring.

DocuWriter.ai
Rector logo

Rector

Your partner for seamless Laravel upgrades, cutting costs, and accelerating innovation for successful companies

Rector

The latest

View all →
October CMS v3.6 Ships Today, Full of New Features image

October CMS v3.6 Ships Today, Full of New Features

Read article
Laracon EU Videos are now out image

Laracon EU Videos are now out

Read article
Use Google's Gemini AI in Laravel image

Use Google's Gemini AI in Laravel

Read article
PhpStorm is getting a brand new terminal image

PhpStorm is getting a brand new terminal

Read article
Six Essential Plugins for Visual Studio Code image

Six Essential Plugins for Visual Studio Code

Read article
Modularize Your Laravel Application With the Modular Package image

Modularize Your Laravel Application With the Modular Package

Read article