sebastiaanluca/laravel-auto-morph-map
Installation:
composer require sebastiaanluca/laravel-auto-morph-map
Publish the config file (if needed):
php artisan vendor:publish --provider="SebastiaanLuca\AutoMorphMap\AutoMorphMapServiceProvider"
Basic Configuration:
Edit config/automorphmap.php to define your morph map aliases. Example:
'morph_map' => [
'App\Models\Post' => 'post',
'App\Models\Video' => 'video',
'App\Models\User' => 'user',
],
First Use Case:
Define a polymorphic relationship in your Comment model:
public function commentable()
{
return $this->morphTo();
}
Now, when querying Comment::find(1)->commentable, the commentable_type column will store 'post' or 'video' instead of the full class name.
Consistent Database Schema:
Use the package to enforce uniform *_type column values across environments (e.g., post instead of App\Models\Post or App\Entities\Post). Example:
// config/automorphmap.php
'morph_map' => [
'App\Models\Post' => 'post',
'App\Entities\Post' => 'post', // Maps to the same alias
],
Dynamic Morph Maps: Override the morph map dynamically in a service provider or boot method:
public function boot()
{
AutoMorphMap::extend(function ($morphMap) {
$morphMap['App\Models\Article'] = 'article';
});
}
Reverse Mapping (Type → Class):
Retrieve the original class from an alias using AutoMorphMap::getModelForType():
$modelClass = AutoMorphMap::getModelForType('post'); // Returns 'App\Models\Post'
Seeding and Factories: Use the aliases in factories or seeders to ensure consistency:
// DatabaseSeeder.php
Comment::factory()->create([
'commentable_type' => 'post',
'commentable_id' => 1,
]);
API Responses: Normalize polymorphic relationships in API responses:
return Comment::with('commentable')->get()->map(function ($comment) {
return [
'id' => $comment->id,
'commentable_type' => $comment->commentable_type, // 'post' or 'video'
'commentable_id' => $comment->commentable_id,
];
});
Middleware for Validation: Validate polymorphic types in middleware or form requests:
public function rules()
{
return [
'commentable_type' => ['required', 'string', Rule::in(array_keys(config('automorphmap.morph_map')))],
];
}
Caching Issues:
config/automorphmap.php:
php artisan config:clear
php artisan route:clear
php artisan view:clear
Case Sensitivity:
The package is case-sensitive by default. Ensure your database values match the config keys exactly (e.g., 'post' vs 'Post').
Missing Aliases: If a model isn’t mapped, Laravel falls back to the full class name. Debug by checking:
dd(AutoMorphMap::getModelForType('unknown')); // Returns null if no match
Dynamic Model Binding: Avoid binding polymorphic models directly by ID without the type, as it may fail silently:
// ❌ Risky: Assumes type is correct
$post = Post::findOrFail($request->commentable_id);
// ✅ Safer: Explicitly resolve
$commentable = AutoMorphMap::resolveModel($request->commentable_type, $request->commentable_id);
Testing: Mock the morph map in tests to avoid hard dependencies:
AutoMorphMap::shouldReceive('getModelForType')
->with('post')
->andReturn('App\Models\Post');
Performance: The package adds minimal overhead. For large-scale apps, preload the morph map in a service provider:
public function boot()
{
AutoMorphMap::load(config('automorphmap.morph_map'));
}
Custom Resolvers: Extend the resolver logic for complex scenarios (e.g., tenant-aware models):
AutoMorphMap::extendResolver(function ($type) {
return "App\\Tenants\\{$type}Model";
});
Environment-Specific Maps: Use environment variables or conditional logic to switch morph maps:
'morph_map' => env('APP_ENV') === 'local'
? ['App\Models\Post' => 'local_post']
: ['App\Models\Post' => 'prod_post'],
Database Migrations:
When updating the morph map, run a data migration to update existing *_type values:
DB::table('comments')->where('commentable_type', 'App\Models\Post')->update([
'commentable_type' => 'post',
]);
Laravel Scout Integration: Use aliases in Scout full-text searchable fields:
public function toSearchableArray()
{
return [
'commentable_type' => $this->commentable_type, // 'post' or 'video'
];
}
API Contracts: Document polymorphic types in OpenAPI/Swagger specs:
commentable_type:
type: string
enum: [post, video, user]
description: Alias of the related model.
How can I help you explore Laravel packages today?