How to fix the Laravel “No morph map defined for model” error

Learn about a common & undocumented caveat with morph maps.

Ralph J. Smit Laravel Software Engineer

Jump immediately to the solution ↓

Laravel Eloquent offers a great way to support polymorphic relationships in your Laravel-application. Laravel does this by providing good support for relations.

As a small recap, polymorphic relationships in Laravel work based on two columns in your database: a column called XXX_type and XXX_id. The column XXX_type refers to the other model that this row refers to. For example, that could be something like App/Models/Video or App/Models/Post. The XXX_id column is the id of the other model. By combining this information, Laravel knows which model it should use.

However, you might see the problem arise here: we know have information about our internal app structure inside our database. What would happen when you move the App/Models/Post class to a new location? You guessed it: the relationships would stop working.

In order to decouple your database from your internal application structure, Laravel offers so-called morph maps. Morph maps basically allow you to register an alias, so that you e.g. had something like post or video in the database, instead of App\Models\Post or App\Models\Video.

The Laravel documentation gives the following example of morph maps.

use Illuminate\Database\Eloquent\Relations\Relation;
 
Relation::enforceMorphMap([
'post' => App\Models\Post::class,
'video' => App\Models\Video::class,
]);

However, have you noticed that the name of the function contains enforce. Why would that be?

If you are relatively new to the Laravel-ecosystem, you might not know that Laravel still offers an alternative method, called Relation::morphMap(). In the past, this was the de facto way to register morph maps. Unfortunately, currently the Laravel-documentation doesn't mention this method anymore, though it still exists.

What is the reason? The reason is that Laravel added a new feature that would disallow relations where there isn't a morph map registered.

Simply put, that basically means that Laravel will throw an exception every time you try to use a polymorphic relationship without a new, specific key.

This feature is enabled by using the Relation::enforceMorphMap() instead of Relation::morphMap(). Laravel recommends to use this protection by default, so it doesn't write about the regular Relation::morphMany() method in the docs.

How to fix the "No morph map defined for model" error in Laravel

Now that you all know this, you can solve the error in two ways:

  1. Add a new key to your Relation::enforceMorphMap() with the model that is causing the error.
  2. Replace the call to Relation::enforceMorphMap() with Relation::morphMap(). This will not require the morph maps anymore for your models.

The new code for solution #2 looks like this:

Relation::morphMap([
'post' => App\Models\Post::class,
'video' => App\Models\Video::class,
]);

Wrapping up

The perfect situation for using this solution is when you want to use morph maps in a Laravel package. In such a situation, you certainly shouldn't force the complete application to use morph maps.

That was also my problem (I sell a premium Laravel-plugin for Filament PHP), because it forced the users to always add a morph map.

There is of course a reason that the Laravel docs do not mention the basic Relation::morphMap(), so the summary is:

  1. Use Relation::enforceMorphMap() if you're using morph maps in your Laravel-application.
  2. Use Relation::morphMap() if you're using morph maps in a Laravel-package.

Published by Ralph J. Smit on in Laravel . Last updated on 22 February 2023 .