cline/morphism
Central registry for Laravel polymorphic key mapping. Define which primary key column (id/uuid/ulid) each model uses in morph relations, with migration macros, optional strict enforcement, and config-based setup—ideal for package authors.
Installation:
composer require cline/morphism
Add to config/app.php under providers if not auto-discovered:
Cline\Morphism\MorphismServiceProvider::class,
First Use Case:
Define polymorphic mappings in a config file (e.g., config/morphism.php):
'mappings' => [
'user' => [
'App\Models\User' => 'id',
'App\Models\Admin' => 'admin_id',
],
'post' => [
'App\Models\Post' => 'post_id',
'App\Models\Draft' => 'draft_id',
],
],
Use the facade in a controller or service:
use Cline\Morphism\Facades\Morphism;
$mappedId = Morphism::map('user', 1, App\Models\User::class); // Returns `1` if `User` uses `id`
config/morphism.php for default mappings.Morphism facade for quick usage.Cline\Morphism\Contracts\MorphismManager for custom implementations.tests/ for edge-case examples (e.g., fallback logic).Polymorphic Key Mapping:
Morphism::map($type, $value, $modelClass) to resolve keys dynamically.user_id to Eloquent’s id or a custom admin_id:
$userId = Morphism::map('user', $legacyId, App\Models\Admin::class);
Reverse Mapping:
Morphism::reverseMap($type, $modelClass, $value) to convert back:
$legacyId = Morphism::reverseMap('user', App\Models\Admin::class, 42);
Dynamic Mappings:
Morphism::extend('user', [
'App\Models\Guest' => 'guest_token',
]);
Integration with Eloquent:
boot() for model events:
public function boot()
{
\App\Models\User::created(function ($user) {
Morphism::map('user', $user->id, self::class); // Log mapping
});
}
API Responses:
$response = [
'user' => Morphism::map('user', $user->id, get_class($user)),
'posts' => array_map(fn ($post) => Morphism::map('post', $post->id, get_class($post)), $posts),
];
map() calls with Model::class.config/morphism.php:
'fallback' => [
'default_key' => 'id',
'throw_on_missing' => false,
],
Morphism::enableCache();
Missing Mappings:
throw_on_missing is false, unmapped types return null. Debug with:
Morphism::hasMapping('user', App\Models\Unknown::class); // Returns bool
throw_on_missing to true.Circular Dependencies:
User → Admin → User). Use reverseMap() sparingly.Case Sensitivity:
get_class($model) instead of hardcoded strings.Octane/Static State:
static::$morpher).Configuration Overrides:
extend()) take precedence over config. Clear cache after changes:
php artisan config:clear
Morphism::debug(function ($type, $model, $key) {
\Log::debug("Mapped $type:$model->id to $key");
});
dd(Morphism::getMappings('user')); // Returns array of [Model => key]
Custom Resolvers:
Implement Cline\Morphism\Contracts\Resolver for logic beyond key mapping:
class CustomResolver implements Resolver {
public function resolve($type, $value, $modelClass) {
// Custom logic (e.g., API token generation)
}
}
Register via:
Morphism::resolver(new CustomResolver());
Event Hooks:
Listen for morphism.mapping events to intercept/reset mappings:
event(new MorphismMappingEvent('user', App\Models\User::class, 1));
Testing:
Mock the MorphismManager in unit tests:
$this->partialMock(MorphismManager::class, function ($mock) {
$mock->shouldReceive('map')->andReturn(999);
});
extend() calls:
Morphism::clearCache();
Morphism::batchMap() for bulk conversions:
$mapped = Morphism::batchMap('user', [1, 2, 3], App\Models\User::class);
How can I help you explore Laravel packages today?