In this guide you build a small profile domain and use the hydrator to convert its objects into plain arrays and back. Everything you see here works without any configuration, the hydrator figures out the normalizers on its own.
We start with a backed enum for the role, a Skill value object and a
ProfileCreated event that combines them. All classes are final, readonly
and use constructor property promotion, the hydrator supports all of it.
enum Role: string
{
case Admin = 'admin';
case Member = 'member';
}
final readonly class Skill
{
public function __construct(
public string $name,
public int $level,
) {
}
}
final readonly class ProfileCreated
{
/** @param list<Skill> $skills */
public function __construct(
public int $id,
public string $name,
public Role $role,
public array $skills,
public DateTimeImmutable $createdAt,
) {
}
}The @param list<Skill> docblock is what tells the hydrator the element type of
the collection. How types are resolved into normalizers is explained on the
normalizer page.
The recommended way to create a hydrator is the StackHydratorBuilder together
with the CoreExtension, which registers the default middleware and the
built-in normalizer guesser.
use Patchlevel\Hydrator\CoreExtension;
use Patchlevel\Hydrator\StackHydratorBuilder;
$hydrator = (new StackHydratorBuilder())
->useExtension(new CoreExtension())
->build();The builder is also the place to add extensions, custom guessers and a metadata cache.
To convert an object into a serializable array, use the extract method.
$event = new ProfileCreated(
1,
'patchlevel',
Role::Admin,
[new Skill('php', 10), new Skill('event-sourcing', 10)],
new DateTimeImmutable('2023-10-01 12:00:00'),
);
$data = $hydrator->extract($event);The result looks like this:
[
'id' => 1,
'name' => 'patchlevel',
'role' => 'admin',
'skills' => [
['name' => 'php', 'level' => 10],
['name' => 'event-sourcing', 'level' => 10],
],
'createdAt' => '2023-10-01T12:00:00+00:00',
]You can now turn this array into JSON with json_encode and store it anywhere.
The process can be reversed with the hydrate method. You pass the class that
should be created and the data that should be written into it.
$event = $hydrator->hydrate(
ProfileCreated::class,
[
'id' => 1,
'name' => 'patchlevel',
'role' => 'admin',
'skills' => [
['name' => 'php', 'level' => 10],
['name' => 'event-sourcing', 'level' => 10],
],
'createdAt' => '2023-10-01T12:00:00+00:00',
],
);The constructor is not called during hydration. The properties are written directly, so constructor validation does not run. You can find more details on the hydrator page.
You can now round-trip arbitrarily nested objects: enums, date types, collections and nested value objects are handled automatically. When the automatic resolution is not enough, you attach a normalizer to the property or class, and that is usually all the configuration you ever need.