Installation
composer require aljerom/simple-hydrator
php -v) is 8.3+ (required for named arguments and enums).First Hydration
use Aljerom\SimpleHydrator\Hydrator;
$hydrator = new Hydrator();
$arrayData = [
'first_name' => 'John',
'last_name' => 'Doe',
'email' => '[email protected]'
];
$user = $hydrator->hydrate($arrayData, User::class);
// $user->firstName, $user->lastName, $user->email are now populated
Where to Look First
src/Hydrator.php (core logic, ~50 lines).tests/HydratorTest.php (edge cases like nested arrays, private properties).Request hydration or API Resource transformation.Use Case: Convert validated request data into domain objects.
use Aljerom\SimpleHydrator\Hydrator;
use Illuminate\Http\Request;
public function store(Request $request, Hydrator $hydrator) {
$validated = $request->validate([
'first_name' => 'required|string',
'last_name' => 'required|string',
]);
$user = $hydrator->hydrate($validated, User::class);
// $user->firstName, $user->lastName are auto-populated
}
Use Case: Convert snake_case API responses to camelCase objects.
public function getUsers() {
$apiResponse = Http::get('https://api.example.com/users')->json();
$users = array_map(
fn($user) => (new Hydrator())->hydrate($user, UserDto::class),
$apiResponse['data']
);
return response()->json($users);
}
Use Case: Replace manual new Entity($array) in services.
public function createUser(array $data) {
$user = (new Hydrator())->hydrate($data, User::class);
$user->save(); // Eloquent or custom persistence
}
Use Case: Hydrate arrays containing nested arrays/objects.
$data = [
'user' => [
'name' => 'John',
'address' => ['street' => '123 Main']
]
];
$user = (new Hydrator())->hydrate($data, UserWithAddress::class);
// $user->user->name, $user->user->address->street
Use Case: Register hydrator as a singleton for dependency injection.
// app/Providers/AppServiceProvider.php
public function register() {
$this->app->singleton(Hydrator::class, fn() => new Hydrator());
}
Usage:
public function __construct(private Hydrator $hydrator) {}
Use Case: Override default type casting (e.g., strings to dates).
$hydrator = new Hydrator();
$hydrator->setTypeMap([
'created_at' => DateTime::class,
'is_active' => 'bool',
]);
$data = ['created_at' => '2023-01-01'];
$model = $hydrator->hydrate($data, Model::class);
// $model->createdAt is now a DateTime object
Use Case: Hydrate arrays of data into collections of objects.
use Illuminate\Support\Collection;
$users = collect($apiData['users'])
->map(fn($user) => (new Hydrator())->hydrate($user, User::class))
->values();
Use Case: Hydrate validated data directly into models.
public function update(Request $request, User $user) {
$validated = $request->validate([
'first_name' => 'sometimes|string',
'last_name' => 'sometimes|string',
]);
(new Hydrator())->hydrate($validated, $user);
$user->save();
}
Use Case: Hydrate objects with dynamic properties (e.g., from JSON).
$dynamicData = json_decode($jsonString, true);
$object = (new Hydrator())->hydrate($dynamicData, stdClass::class);
Use Case: Simplify unit tests by hydrating mock data.
public function testUserCreation() {
$data = ['first_name' => 'Test', 'last_name' => 'User'];
$user = (new Hydrator())->hydrate($data, User::class);
$this->assertEquals('Test', $user->firstName);
}
Private/Protected Properties
setAccessibleProperties() to include them:
$hydrator->setAccessibleProperties(true);
Circular References
$hydrator = new class extends Hydrator {
protected int $depth = 0;
protected const MAX_DEPTH = 5;
public function hydrate(array $data, string $class, ?array $options = null): object {
if ($this->depth >= self::MAX_DEPTH) {
throw new \RuntimeException('Max hydration depth exceeded');
}
$this->depth++;
try {
return parent::hydrate($data, $class, $options);
} finally {
$this->depth--;
}
}
};
Type Mismatches
"1" → 1) may cause bugs.$hydrator->setTypeMap(['id' => 'int', 'is_active' => 'bool']);
Overwriting Existing Properties
merge mode for partial updates:
$hydrator->hydrate($data, $existingObject, ['mode' => 'merge']);
Performance with Large Arrays
$cachedHydrator = fn($data) => (new Hydrator())->hydrate($data, User::class);
Non-Standard Naming Conventions
snake_case→camelCase may conflict with existing code.convertPropertyName():
class CustomHydrator extends Hydrator {
protected function convertPropertyName(string $name): string {
return str_replace('_', '', $name); // PascalCase
}
}
Laravel Model Binding Conflicts
// Bad: May conflict with route model binding
$user = (new Hydrator())->hydrate($data, User::class);
// Good: Explicit new instance
$user = new User();
(new Hydrator())->hydrate($data, $user);
PHP 8.3+ Features
v1.0.0 or use polyfills.$hydrator = new class extends Hydrator {
public function hydrate(array $data, string $class, ?array $
How can I help you explore Laravel packages today?