PHP code example of jakubboucek / hydrator

1. Go to this page and download the library: Download jakubboucek/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/ */

    

jakubboucek / hydrator example snippets


use JakubBoucek\Hydrator\HydratorFactory;
use JakubBoucek\Hydrator\Format\NetteDatabase;

$factory = new HydratorFactory(
    format: NetteDatabase::class,                 // preferred format
    timeZone: new DateTimeZone('Europe/Prague'),  // app time zone (defaults to PHP default)
);

$articles = $factory->for(Article::class);

// single row: array or Traversable (Nette Row / ActiveRow)
$article = $articles->fromData($explorer->table('article')->get(1));

// whole result: lazy single-pass stream; a Selection keys it by its primary key
foreach ($articles->fromDataSet($explorer->table('article')) as $id => $article) {
    // ...
}

// partial update: only initialized properties are extracted
$patch = new Article();
$patch->title = 'Updated title';
$explorer->table('article')->where('id', $id)->update($articles->toData($patch));

use JakubBoucek\Hydrator\Attribute\Type;
use JakubBoucek\Hydrator\Entity;

class Article implements Entity
{
    public int $id;
    public string $title;
    public ?string $note;
    public bool $published;
    public DateTimeImmutable $createdAt;

    #[Type\Date]                        // DATE column: no time part
    public DateTimeImmutable $publishedOn;

    public DateInterval $readingTime;   // TIME column
    public ArticleStatus $status;       // BackedEnum, mapped by backing value

    public string $label {              // virtual property: ignored by the hydrator
        get => "#{$this->id} {$this->title}";
    }
}

// stream keyed by the source (Selection = primary key)
foreach ($articles->fromDataSet($explorer->table('article')) as $id => $article) { /* … */ }

// re-keyed by a property, materialized into a lookup map
$byId = $articles->fromDataSet($pdo->query('SELECT * FROM article'), keyBy: 'id')->collectMap();

// materialized into a plain list, keys dropped
$today = $items->fromDataSet($rows)->collectList();

$favorite->position ??= $this->nextPosition($favorite->userId);  // fallback only when not set

if (isset($entity->id)) { /* update */ } else { /* insert */ }   // upsert dispatch

$hydrator->isInitialized($entity, 'note');       // true also for a stored null
$hydrator->getInitializedPropertyNames($entity); // e.g. ['id', 'note'] — names only, never values
$hydrator->getInitializationState($entity);      // InitializationState::Empty / Partial / Complete

$data = $hydrator->toData($changes);
if ($data === []) {
    return;
}
$table->update($data);

class UpperSnake extends Mysql
{
    protected function createNameConverter(): NameConverter
    {
        return new MyUpperSnakeConverter();
    }
}

use JakubBoucek\Hydrator\Attribute\Name;
use JakubBoucek\Hydrator\Format\DatabaseFormat;

class Legacy
{
    #[Name('some__name', [DatabaseFormat::class])]  // all database formats
    #[Name('someName')]                             // any other format
    public string $someName;
}

#[Fraction(6)]                          // DATETIME(6): always six places
public DateTimeImmutable $measuredAt;

#[Fraction(3, omitZero: true, formats: [Json::class])]  // milliseconds, only when non-zero
public DateTimeImmutable $processedAt;

#[DateFormat('U')]                      // unix timestamp
public DateTimeImmutable $syncedAt;

#[Fraction(3, formats: [DatabaseFormat::class])]
#[DateFormat('d.m.Y H:i', formats: [Json::class])]
public DateTimeImmutable $mixedUse;

use JakubBoucek\Hydrator\Struct\BaseStruct;
use JakubBoucek\Hydrator\Entity;
use JakubBoucek\Hydrator\Struct\NoteList;

class AddressStruct extends BaseStruct
{
    public ?string $city = null;
    public ?string $street = null;
    public ?string $zip = null;
}

class Member implements Entity
{
    public int $id;
    public AddressStruct $address;   // non-nullable: an instance always exists
    public NoteList $notes;
}

$member = $members->fromData($row);
$member->address->city = 'Plzeň';    // writable at any time, no null-checks
$member->notes->add('Paid by wire', 'admin', new DateTimeImmutable());

class Device implements Entity
{
    public int $id;
    public JsonObject $config;
}

$device = $hydrator->fromData($row);
$device->config->value['mode'] = 'boost';   // a NULL column hydrated into an
$device->config->value['tags'][] = 'new';   // empty instance — no null checks
$explorer->table('device')->where('id', $device->id)->update($hydrator->toData($device));

use JakubBoucek\Hydrator\Struct\RawJsonObject;

class WebhookPayload extends RawJsonObject
{
    public function getEventName(): string
    {
        return $this->getString('event');                       // strict: missing/null throws
    }

    public function getRepositoryName(): ?string
    {
        return $this->tryGetString(['repository', 'full_name']); // tolerant: missing/null → null
    }
}

use JakubBoucek\Hydrator\Value\IntValue;

final class Money implements IntValue
{
    private function __construct(
        public readonly int $cents,
    ) {}

    public static function fromNative(int $value): static   // exact type, no unions
    {
        return new static($value);
    }

    public function toNative(): ?int
    {
        return $this->cents;
    }
}

use JakubBoucek\Hydrator\Value\NativeType;
use JakubBoucek\Hydrator\Adapter\TypeAdapter;

final class UuidAdapter implements TypeAdapter
{
    public static function provides(): array
    {
        return [
            UuidInterface::class => NativeType::String,
            LazyUuidFromString::class => NativeType::String,
        ];
    }

    public function import(mixed $value, string $targetClass): object
    {
        return Uuid::fromString((string) $value);
    }

    public function export(object $value): string
    {
        return (string) $value;
    }
}

$factory = new HydratorFactory(
    format: NetteDatabase::class,
    adapters: [UuidAdapter::class, new GeoAdapter($resolver)],
);

use JakubBoucek\Hydrator\Value\JsonValue;
use JakubBoucek\Hydrator\Value\StringValue;

final class Point implements StringValue, JsonValue
{
    // fromNative()/toNative(): 'lat;lng' string for the database …

    public static function fromJsonValue(mixed $value): static
    {
        if (!is_array($value) || !is_numeric($value['lat'] ?? null) || !is_numeric($value['lng'] ?? null)) {
            throw new ValueException('Expected {lat, lng} object, got ' . get_debug_type($value) . '.');
        }

        return new static((float) $value['lat'], (float) $value['lng']);
    }

    public function toJsonValue(): mixed
    {
        return ['lat' => $this->lat, 'lng' => $this->lng];
    }
}

class Event implements Entity
{
    public int $id;
    public ?RawJsonValue $payload;
}

$event = $hydrator->fromData($row);
$event->payload?->getType();      // JsonType::Object | List | String | Number | Bool | Null
$event->payload?->isObject();     // sugar: isObject/isList/isString/isNumber/isBool/isNull
$event->payload?->getValue();     // lazy-decoded value (arrays for objects), cached

$event->payload = RawJsonValue::fromJsonValue(['a' => 1]);  // a new document is a new instance