How to use a custom morph class in Laravel?

Laravel polymorphism is a quite elegant solution. Yesterday, I came up with a problem with the *able_type column value when using extended versions of the model.

The Problem

Most our code at HYVOR has something called access objects:

1// Project.php
2class Project extends Model
3{}
4
5// AccessProject.php
6class AccessProject extends Project
7{}

It simply extends a Laravel model. Then, in routes like /project/{projectId}, we set the access object in the service container:

1class ProjectMiddleware
2{
3 public function handle($request, $next)
4 {
5
6 $projectId = $request->route('projectId');
7
8 $project = Project::find($projectId);
9
10 if (!$project) {
11 throw new Exception('Project not found');
12 }
13
14 app()->instance(
15 AccessProject::class,
16 (new AccessProject())->newInstance(
17 $project->getAttributes(),
18 true
19 )
20 );
21
22 return $next($request);
23 }
24}

Then, we can easily use the AccessProject object in our controllers, in a Laravel-ish way (using injection).

1class ProjectController
2{
3
4 public function update(AccessProject $project)
5 {
6 // project
7 }
8
9}

However, in the morph table, the class name varies depending on the currently used class.

The Solution

The solution is as follows.

First, set the getMorphClass method in the model. You can return the class name or just another string.

1class Project
2{
3
4 public function getMorphClass()
5 {
6 return 'project';
7 }
8
9}

If you return the full class name, the next step is not needed. Here, we set the morph map in the app service provider.

1class AppServiceProvider
2{
3 public function boot()
4 {
5 Relation::morphMap([
6 'project' => Project::class,
7 ]);
8 }
9}

That’s it!