Installation:
composer require sofa/eloquence-mappable
Publish the config (if needed):
php artisan vendor:publish --provider="Sofa\Eloquence\EloquenceServiceProvider"
Enable Mappable:
Extend your Eloquent model with Sofa\Eloquence\Mappable\MappableTrait:
use Sofa\Eloquence\Mappable\MappableTrait;
class User extends Model
{
use MappableTrait;
}
First Use Case: Define a mapping in your model:
protected $mappable = [
'full_name' => 'name', // Maps 'full_name' to 'name' field
'email_address' => 'email',
'user_profile' => ['profile_id' => 'profile.id'], // Maps to related model
];
Use it to map an array:
$mappedData = User::map(['full_name' => 'John Doe', 'email_address' => 'john@example.com']);
$user = User::create($mappedData);
Basic Attribute Mapping:
// Map incoming data to model attributes
$data = ['first_name' => 'Jane', 'last_name' => 'Doe'];
$mapped = User::map($data, ['full_name' => 'first_name last_name']);
Related Model Mapping:
// Map nested data to related models (e.g., Profile)
$mappable = [
'user_profile' => [
'bio' => 'bio',
'avatar' => 'avatar_url',
],
];
Dynamic Mapping:
Use map() in controllers or services:
public function store(Request $request)
{
$validated = $request->validate([...]);
$user = User::map($validated)->create();
}
Bulk Mapping:
$users = collect([...])->map(fn($data) => User::map($data)->create());
map() in API requests to transform incoming JSON into model attributes.map() before saving records.$mapped = Cache::remember("user_{$id}_mapped", now()->addHours(1), fn() => User::find($id)->map($data));
Circular References:
Avoid circular mappings (e.g., A maps to B, B maps back to A). Use explicit ignore in $mappable:
protected $mappableIgnore = ['related_model'];
Overwriting Mutators:
If your model has custom accessors/mutators, ensure they don’t conflict with map() logic. Override map() if needed:
public function map(array $data, array $mappable = null)
{
$data = parent::map($data, $mappable);
// Custom logic here
return $data;
}
Mass Assignment:
map() does not automatically whitelist fields for fillable. Explicitly define $fillable or use guarded.
Performance: Deeply nested mappings (e.g., 3+ levels) can slow queries. Optimize with eager loading:
User::with('profile')->map($data);
dd(User::getMappable($data));
map() fails silently, enable debug mode or check for missing keys in $mappable.Custom Mappers: Extend the mapper class:
class CustomMapper extends \Sofa\Eloquence\Mappable\Mapper
{
protected function transform($key, $value) { ... }
}
Bind it in AppServiceProvider:
Eloquence::extend('custom', function () {
return new CustomMapper();
});
Dynamic Mappings: Use closures for runtime mappings:
protected $mappable = [
'dynamic_field' => function ($value) {
return strtoupper($value);
},
];
Event Hooks:
Listen to eloquence.mapping events for logging/auditing:
Event::listen('eloquence.mapping', function ($model, $data, $mapped) {
// Log or audit changes
});
config/eloquence.php:
'mapper' => \App\CustomMapper::class,
strict mode to throw errors on missing keys:
User::map($data, null, ['strict' => true]);
How can I help you explore Laravel packages today?