PHP code example of patchlevel / hydrator

1. Go to this page and download the library: Download patchlevel/hydrator library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

patchlevel / hydrator example snippets


use Patchlevel\Hydrator\MetadataHydrator;

$hydrator = MetadataHydrator::create();

final readonly class ProfileCreated 
{
    /**
     * @param list<Skill> $skills
     */
    public function __construct(
        public int $id,
        public string $name,
        public Role $role, // enum,
        public array $skills, // array of objects
        public DateTimeImmutable $createdAt,
    ) {
    }
}

$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);

[
  'id' => 1,
  'name' => 'patchlevel',
  'role' => 'admin',
  'skills' => [
    [
      'name' => 'php',
      'level' => 10,
    ],
    [
      'name' => 'event-sourcing',
      'level' => 10,
    ],
  ],
  'createdAt' => '2023-10-01T12:00:00+00:00',
]

$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',
    ]
);

$oldEvent == $event // true

use Patchlevel\Hydrator\Normalizer\ArrayNormalizer;
use Patchlevel\Hydrator\Normalizer\DateTimeImmutableNormalizer;

final class DTO 
{
    #[ArrayNormalizer(new DateTimeImmutableNormalizer())]
    public array $dates;
}

use Patchlevel\Hydrator\Normalizer\DateTimeImmutableNormalizer;

final class DTO 
{
    #[DateTimeImmutableNormalizer]
    public DateTimeImmutable $date;
}

use Patchlevel\Hydrator\Normalizer\DateTimeImmutableNormalizer;

final class DTO 
{
    #[DateTimeImmutableNormalizer(format: DateTimeImmutable::RFC3339_EXTENDED)]
    public DateTimeImmutable $date;
}

use Patchlevel\Hydrator\Normalizer\DateTimeNormalizer;

final class DTO 
{
    #[DateTimeNormalizer]
    public DateTime $date;
}

use Patchlevel\Hydrator\Normalizer\DateTimeNormalizer;

final class DTO 
{
    #[DateTimeNormalizer(format: DateTime::RFC3339_EXTENDED)]
    public DateTime $date;
}

use Patchlevel\Hydrator\Normalizer\DateTimeZoneNormalizer;

final class DTO
{
    #[DateTimeZoneNormalizer]
    public DateTimeZone $timeZone;
}

use Patchlevel\Hydrator\Normalizer\EnumNormalizer;

final class DTO
{
    #[EnumNormalizer]
    public Status $status;
}

use Patchlevel\Hydrator\Normalizer\ObjectNormalizer;

final class DTO
{
    #[ObjectNormalizer]
    public AnohterDto $anotherDto;
    
    #[ObjectNormalizer(AnohterDto::class)]
    public object $object;
}

final class AnotherDto
{
    #[EnumNormalizer]
    public Status $status;
}

final class Name
{
    private string $value;
    
    public function __construct(string $value) 
    {
        if (strlen($value) < 3) {
            throw new NameIsToShortException($value);
        }
        
        $this->value = $value;
    }
    
    public function toString(): string 
    {
        return $this->value;
    }
}

use Patchlevel\Hydrator\Normalizer\Normalizer;
use Patchlevel\Hydrator\Normalizer\InvalidArgument;

#[Attribute(Attribute::TARGET_PROPERTY | Attribute::TARGET_CLASS)]
class NameNormalizer implements Normalizer
{
    public function normalize(mixed $value): string
    {
        if (!$value instanceof Name) {
            throw InvalidArgument::withWrongType(Name::class, $value);
        }

        return $value->toString();
    }

    public function denormalize(mixed $value): ?Name
    {
        if ($value === null) {
            return null;
        }

        if (!is_string($value)) {
            throw InvalidArgument::withWrongType('string', $value);
        }

        return new Name($value);
    }
}

final class DTO
{
    #[NameNormalizer]
    public Name $name
}

#[NameNormalizer]
final class Name
{
    // ... same as before
}

use Patchlevel\Hydrator\Guesser\Guesser;
use Symfony\Component\TypeInfo\Type\ObjectType;

class NameGuesser implements Guesser
{
    public function guess(ObjectType $object): Normalizer|null
    {
        return match($object->getClassName()) {
            case Name::class => new NameNormalizer(),
            default => null,
        };
    }
}

use Patchlevel\Hydrator\MetadataHydrator;

$hydrator = MetadataHydrator::create([new NameGuesser()]);

use Patchlevel\Hydrator\Attribute\NormalizedName;

final class DTO
{
    #[NormalizedName('profile_name')]
    public string $name
}

[
  'profile_name' => 'David'
]

use Patchlevel\Hydrator\Attribute\Ignore;

readonly class ProfileCreated 
{
    public function __construct(
        public string $id,
        public string $name,
        #[Ignore]
        public string $ignoreMe,
    ) {
    }
}

use Patchlevel\Hydrator\Attribute\PostHydrate;
use Patchlevel\Hydrator\Attribute\PreExtract;

readonly class Dto 
{    
    #[PostHydrate]
    private function postHydrate(): void
    {
        // do something
    }
    
    #[PreExtract]
    private function preExtract(): void
    {
        // do something
    }
}

use Patchlevel\Hydrator\Cryptography\PersonalDataPayloadCryptographer;
use Patchlevel\Hydrator\Cryptography\Store\CipherKeyStore;
use Patchlevel\Hydrator\Metadata\Event\EventMetadataFactory;
use Patchlevel\Hydrator\MetadataHydrator;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Patchlevel\Hydrator\Event\PostExtract;
use Patchlevel\Hydrator\Event\PreHydrate;

$eventDispatcher = new EventDispatcher();

$eventDispatcher->addListener(
    PostExtract::class,
    static function (PostExtract $event): void {
        // do something
    }
);

$eventDispatcher->addListener(
    PreHydrate::class,
    static function (PreHydrate $event): void {
        // do something
    }
);

$hydrator = new MetadataHydrator(eventDispatcher: $eventDispatcher);

use Patchlevel\Hydrator\Attribute\DataSubjectId;

final class EmailChanged
{
    public function __construct(
        #[DataSubjectId]
        public readonly string $profileId,
    ) {
    }
}

use Patchlevel\Hydrator\Attribute\DataSubjectId;
use Patchlevel\Hydrator\Attribute\PersonalData;

final class DTO 
{
    public function __construct(
        #[DataSubjectId]
        public readonly string $profileId,
        #[PersonalData]
        public readonly string|null $email,
    ) {
    }
}

use Patchlevel\Hydrator\Attribute\PersonalData;

final class DTO
{
    public function __construct(
        #[DataSubjectId]
        public readonly string $profileId,
        #[PersonalData(fallback: 'unknown')]
        public readonly string $name,
    ) {
    }
}

use Patchlevel\Hydrator\Attribute\DataSubjectId;
use Patchlevel\Hydrator\Attribute\PersonalData;

final class ProfileCreated
{
    public function __construct(
        #[DataSubjectId]
        public readonly string $profileId,
        #[PersonalData(fallback: 'deleted profile')]
        public readonly string $name,
        #[PersonalData(fallbackCallable: [self::class, 'anonymizedEmail'])]
        public readonly string $email,
    ) {
    }
    
    public static function anonymizedEmail(string $subjectId): string
    {
        return sprintf('%[email protected]', $subjectId);
    }
}

use Patchlevel\Hydrator\Cryptography\PersonalDataPayloadCryptographer;
use Patchlevel\Hydrator\Cryptography\Store\CipherKeyStore;
use Patchlevel\Hydrator\Metadata\Event\EventMetadataFactory;
use Patchlevel\Hydrator\MetadataHydrator;

$cipherKeyStore = new InMemoryCipherKeyStore();
$cryptographer = PersonalDataPayloadCryptographer::createWithDefaultSettings($cipherKeyStore);
$hydrator = new MetadataHydrator(cryptographer: $cryptographer);

use Patchlevel\Hydrator\Cryptography\Cipher\CipherKey;
use Patchlevel\Hydrator\Cryptography\Store\InMemoryCipherKeyStore;

$cipherKeyStore = new InMemoryCipherKeyStore();

/** @var CipherKey $cipherKey */
$cipherKeyStore->store('foo-id', $cipherKey);
$cipherKey = $cipherKeyStore->get('foo-id');
$cipherKeyStore->remove('foo-id');

$cipherKeyStore->remove('foo-id');