eventsauce/object-hydrator
Magic-less object hydration and serialization for PHP. Map arrays/decoded JSON to DTOs, commands, queries, and events by inspecting constructors and public getters—no private-property reflection. Supports custom keys, casting, and optional code generation for speed.
Installation:
composer require eventsauce/object-hydrator
Add to composer.json if using a monorepo or custom setup:
"repositories": [{
"type": "vcs",
"url": "https://github.com/EventSaucePHP/ObjectHydrator"
}]
Basic Usage:
use EventSauce\ObjectHydrator\Hydrator;
use EventSauce\ObjectHydrator\Exception\HydrationFailed;
$hydrator = new Hydrator();
$data = ['name' => 'John', 'age' => 30];
$user = $hydrator->hydrate(User::class, $data);
First Use Case: Convert an API response or database row into a strict Laravel Eloquent model or DTO:
$apiResponse = json_decode($httpResponse->getBody(), true);
$user = $hydrator->hydrate(User::class, $apiResponse);
DTO Hydration: Use for strict, immutable data transfer objects:
class UserDTO {
public function __construct(
public string $name,
public int $age,
public ?string $email = null
) {}
}
$dto = $hydrator->hydrate(UserDTO::class, $data);
Eloquent Model Hydration: Bypass Laravel’s mass assignment protection:
$user = $hydrator->hydrate(User::class, $data);
$user->save(); // No need for `$fillable` or `$guarded`
Nested Hydration: Hydrate collections or nested objects:
$data = ['posts' => [['title' => 'Hello'], ['title' => 'World']]];
$user = $hydrator->hydrate(UserWithPosts::class, $data);
Laravel Service Providers:
Bind the hydrator as a singleton in AppServiceProvider:
$this->app->singleton(Hydrator::class, fn() => new Hydrator());
API Request Validation:
Combine with Laravel’s ValidatesRequests:
public function store(Request $request) {
$validated = $request->validate([...]);
$user = resolve(Hydrator::class)->hydrate(User::class, $validated);
}
Event Dispatching: Hydrate event payloads directly:
$event = $hydrator->hydrate(UserRegistered::class, $payload);
event($event);
Type Mismatches:
HydrationFailed on type mismatches (e.g., passing "30" to an int field).Hydrator::withTypeCasting() to auto-cast strings to integers/booleans:
$hydrator = new Hydrator(['cast' => true]);
Circular References:
User → Post → User).Hydrator::withCircularReferenceHandling() (requires PHP 8.1+):
$hydrator = new Hydrator(['circular_reference_handling' => 'ignore']);
Private/Protected Properties:
Hydrator::withPropertyAccess() (PHP 8.0+):
$hydrator = new Hydrator(['property_access' => PropertyAccess::PUBLIC_AND_PRIVATE]);
Enable Strict Mode:
Set ['strict' => true] to fail on unknown properties (useful for DTOs):
$hydrator = new Hydrator(['strict' => true]);
Custom Hydrators:
Extend Hydrator to add logic:
class CustomHydrator extends Hydrator {
protected function hydrateProperty($property, $value, $object) {
if ($property === 'email') {
$value = strtolower($value);
}
return parent::hydrateProperty($property, $value, $object);
}
}
Custom Type Casting:
Override Hydrator::castValue() to handle custom types (e.g., Carbon instances):
$hydrator = new Hydrator(['cast' => function($value, $type) {
return $type === 'DateTime' ? Carbon::parse($value) : null;
}]);
Post-Hydration Callbacks:
Use Hydrator::withPostHydrateCallback():
$hydrator = new Hydrator(['post_hydrate' => fn($object) => $object->boot()]);
Laravel Collections:
Hydrate collections with Collection::mapInto():
$users = collect($data)->mapInto(User::class);
// Internally uses a hydrator-like pattern.
How can I help you explore Laravel packages today?