PHP code example of tiny-blocks / mapper

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

    

tiny-blocks / mapper example snippets




declare(strict_types=1);

final readonly class Amount
{
    public function __construct(public int $amount, public Currency $currency)
    {
    }
}



declare(strict_types=1);

enum Currency: string
{
    case BRL = 'BRL';
    case USD = 'USD';
}



declare(strict_types=1);

use TinyBlocks\Mapper\Mapper;

$mapper = Mapper::create();

$amount = $mapper->toObject(type: Amount::class, source: ['amount' => 50, 'currency' => 'BRL']);
$amount = $mapper->toObject(type: Amount::class, source: '{"amount":50,"currency":"BRL"}');

$missing = $mapper->toObjectOrNull(type: Amount::class, source: null);

$array = $mapper->toArray(source: $amount);
$json = $mapper->toJson(source: $amount);



declare(strict_types=1);

use TinyBlocks\Mapper\Mapper;
use TinyBlocks\Mapper\SnakeCase;

$strict = Mapper::create()
    ->withNaming(namingStrategy: SnakeCase::create())
    ->rejectingUnknownKeys();



declare(strict_types=1);

use TinyBlocks\Mapper\Mappable;
use TinyBlocks\Mapper\MappableBehavior;

final readonly class Address implements Mappable
{
    use MappableBehavior;

    public function __construct(public string $city, public string $street)
    {
    }
}



declare(strict_types=1);

$address = Address::buildFrom(source: ['street' => 'Av. Paulista', 'city' => 'São Paulo']);

$array = $address->toArray();
$json = $address->toJson();



declare(strict_types=1);

abstract readonly class PaymentMethod
{
}



declare(strict_types=1);

final readonly class Pix extends PaymentMethod
{
    public function __construct(public string $payerId)
    {
    }

    public static function pending(): Pix
    {
        return new Pix(payerId: 'pending');
    }
}



declare(strict_types=1);

final readonly class DebitCard extends PaymentMethod
{
    public function __construct(public string $cardNumber)
    {
    }
}



declare(strict_types=1);

use TinyBlocks\Mapper\Mapper;
use TinyBlocks\Mapper\Subtype;

$mapper = Mapper::create()->withMapping(
    type: PaymentMethod::class,
    mapping: Subtype::by(
        field: 'type',
        types: [Pix::class, DebitCard::class],
        default: static fn(): Pix => Pix::pending()
    )
);

$method = $mapper->toObject(type: PaymentMethod::class, source: ['type' => 'pix', 'payerId' => 'Alice']);



declare(strict_types=1);

final readonly class Camera
{
    public function __construct(public string $serialNumber, public int $shotCount)
    {
    }
}



declare(strict_types=1);

final readonly class Studio
{
    public function __construct(public Camera $mainCamera, public string $tag)
    {
    }
}



declare(strict_types=1);

use TinyBlocks\Mapper\Layout;
use TinyBlocks\Mapper\Mapper;
use TinyBlocks\Mapper\SnakeCase;

$mapper = Mapper::create()
    ->withNaming(namingStrategy: SnakeCase::create())
    ->withMapping(type: Studio::class, mapping: Layout::from(paths: []));

$studio = $mapper->toObject(
    type: Studio::class,
    source: [
        'main_camera_serial_number' => 'sn-1',
        'main_camera_shot_count'    => 7,
        'tag'                       => 'studio-a'
    ]
);



declare(strict_types=1);

final readonly class MemberId
{
    public function __construct(private string $value)
    {
    }

    public function value(): string
    {
        return $this->value;
    }
}



declare(strict_types=1);

final readonly class Owner
{
    public function __construct(public MemberId $memberId, public string $name)
    {
    }
}



declare(strict_types=1);

use TinyBlocks\Mapper\JsonColumn;
use TinyBlocks\Mapper\Layout;
use TinyBlocks\Mapper\Mapper;

$mapper = Mapper::create()->withMapping(
    type: Owner::class,
    mapping: Layout::from(paths: ['memberId' => new JsonColumn(column: 'member')])
);

$owner = $mapper->toObject(type: Owner::class, source: ['member' => '{"value":"m-1"}', 'name' => 'Alice']);



declare(strict_types=1);

final readonly class Refund
{
    public function __construct(public string $reference, public Amount $amount)
    {
    }
}



declare(strict_types=1);

use Generator;
use IteratorAggregate;
use TinyBlocks\Mapper\ElementType;
use TinyBlocks\Mapper\IterableMappable;
use TinyBlocks\Mapper\Mapper;

#[ElementType(Refund::class)]
class Refunds implements IteratorAggregate, IterableMappable
{
    private function __construct(public readonly iterable $elements)
    {
    }

    public static function createFrom(iterable $elements): static
    {
        return new static(elements: $elements);
    }

    public function toJson(): string
    {
        return Mapper::create()->toJson(source: $this);
    }

    public function toArray(): array
    {
        return Mapper::create()->toArray(source: $this);
    }

    public function getIterator(): Generator
    {
        foreach ($this->elements as $key => $element) {
            yield $key => $element;
        }
    }
}



declare(strict_types=1);

use TinyBlocks\Mapper\Mapper;

$mapper = Mapper::create();

$refunds = $mapper->toObject(
    type: Refunds::class,
    source: [
        ['reference' => 'r-1', 'amount' => ['amount' => 100, 'currency' => 'BRL']],
        ['reference' => 'r-2', 'amount' => ['amount' => 200, 'currency' => 'BRL']]
    ]
);



declare(strict_types=1);

use DateTimeImmutable;

final readonly class CalendarDate
{
    private function __construct(private DateTimeImmutable $value)
    {
    }

    public static function fromIso(string $iso): CalendarDate
    {
        return new CalendarDate(value: DateTimeImmutable::createFromFormat('!Y-m-d', $iso));
    }

    public function toIso(): string
    {
        return $this->value->format('Y-m-d');
    }
}



declare(strict_types=1);

final readonly class Reservation
{
    public function __construct(public CalendarDate $checkIn)
    {
    }
}



declare(strict_types=1);

use TinyBlocks\Mapper\Codec;
use TinyBlocks\Mapper\Mapper;

$mapper = Mapper::create()->withMapping(
    type: CalendarDate::class,
    mapping: Codec::from(
        decode: static fn(string $iso): CalendarDate => CalendarDate::fromIso(iso: $iso),
        encode: static fn(CalendarDate $date): string => $date->toIso()
    )
);

# The encode closure drives every write, so the nested date stays a canonical YYYY-MM-DD string.
$row = $mapper->toArray(source: new Reservation(checkIn: CalendarDate::fromIso(iso: '2026-05-23')));

# A bare scalar is decoded directly into the value object.
$checkIn = $mapper->toObject(type: CalendarDate::class, source: '2026-05-23');



declare(strict_types=1);

use TinyBlocks\Mapper\ScalarCodec;

#[ScalarCodec(decode: 'fromLabel', encode: 'toLabel')]
#[ScalarCodec(decode: 'fromNumber', encode: 'toLabel')]
final readonly class Version
{
    private function __construct(private string $label)
    {
    }

    public static function fromLabel(string $label): Version
    {
        return new Version(label: $label);
    }

    public static function fromNumber(int $number): Version
    {
        return new Version(label: (string) $number);
    }

    public function toLabel(): string
    {
        return $this->label;
    }
}



declare(strict_types=1);

final readonly class Release
{
    public function __construct(public Version $version)
    {
    }
}



declare(strict_types=1);

use TinyBlocks\Mapper\Mapper;

$mapper = Mapper::create();

# The string source selects the decode whose parameter is typed string.
$fromLabel = $mapper->toObject(type: Version::class, source: 'v7');

# The integer source, nested in a release, selects the decode whose parameter is typed int.
$release = $mapper->toObject(type: Release::class, source: ['version' => 7]);

# The first declared pair's encode renders the scalar form.
$label = $mapper->toArray(source: Version::fromLabel(label: 'v7'));



declare(strict_types=1);

enum Priority: string
{
    case LOW = 'low';
    case HIGH = 'high';
}

final readonly class Label
{
    public function __construct(public Priority $priority)
    {
    }
}



declare(strict_types=1);

final readonly class Task
{
    public function __construct(public string $name, public Label $label)
    {
    }
}



declare(strict_types=1);

use TinyBlocks\Mapper\Mapper;

$mapper = Mapper::create();

# The label adopts the backed enum's scalar form, so the column holds a bare value.
$row = $mapper->toArray(source: new Task(name: 'deploy', label: new Label(priority: Priority::HIGH)));

# The same scalar reconstructs the nested label.
$task = $mapper->toObject(type: Task::class, source: ['name' => 'deploy', 'label' => 'high']);



declare(strict_types=1);

final readonly class Organization
{
    public function __construct(public string $registrationId)
    {
    }
}



declare(strict_types=1);

use TinyBlocks\Mapper\Mapper;
use TinyBlocks\Mapper\SnakeCase;
use TinyBlocks\Mapper\Structured;

# Default delegation collapses the wrapper to its inner scalar.
Mapper::create()->toArray(source: new Organization(registrationId: 'org-1'));
# ['org-1']

# Registered with Structured, the wrapper keeps its object shape and rebuilds on read.
$mapper = Mapper::create()->withMapping(type: Organization::class, mapping: Structured::create());

$mapper->toArray(source: new Organization(registrationId: 'org-1'));
# ['registrationId' => 'org-1']

$mapper->toObject(type: Organization::class, source: ['registrationId' => 'org-1']);
# Organization(registrationId: 'org-1')

# It composes with the naming strategy and with omittingNulls.
Mapper::create()
    ->withNaming(namingStrategy: SnakeCase::create())
    ->withMapping(type: Organization::class, mapping: Structured::create())
    ->toArray(source: new Organization(registrationId: 'org-1'));
# ['registration_id' => 'org-1']



declare(strict_types=1);

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

    public static function of(int $cents, string $currency): Money
    {
        return new Money(cents: $cents, currency: strtoupper($currency));
    }
}



declare(strict_types=1);

use TinyBlocks\Mapper\FactoryMethod;
use TinyBlocks\Mapper\Mapper;

$mapper = Mapper::create()->withMapping(
    type: Money::class,
    mapping: FactoryMethod::using(method: 'of')
);

# Each parameter is resolved from the array by its name, then passed to the factory.
$money = $mapper->toObject(type: Money::class, source: ['cents' => 500, 'currency' => 'brl']);

# Reflection over the declared properties writes the array back, symmetric with the read input.
$row = $mapper->toArray(source: $money);



declare(strict_types=1);

use DateTimeImmutable;

final readonly class Profile
{
    public function __construct(
        public string $name,
        public ?string $title,
        public DateTimeImmutable $createdAt,
        public Severity $severity
    ) {
    }
}



declare(strict_types=1);

enum Severity
{
    case LOW;
    case HIGH;
}



declare(strict_types=1);

use TinyBlocks\Mapper\Configuration;
use TinyBlocks\Mapper\Mapper;

$mapper = Mapper::create();

$profile = $mapper->toObject(
    type: Profile::class,
    source: [
        'name'      => 'Alice',
        'title'     => 'Owner',
        'createdAt' => '2026-01-01T00:00:00+00:00',
        'severity'  => 'HIGH'
    ]
);

$array = $mapper->toArray(source: $profile, configuration: Configuration::default()->omitting('title'));

$reindex = $mapper->toArray(source: $refunds, configuration: Configuration::default()->discardingKeys());

$draft = $mapper->toObject(
    type: Profile::class,
    source: [
        'name'      => 'Alice',
        'title'     => null,
        'createdAt' => '2026-01-01T00:00:00+00:00',
        'severity'  => 'HIGH'
    ]
);

# The explicit null title is dropped, so the output is:
# ['name' => 'Alice', 'createdAt' => '2026-01-01T00:00:00+00:00', 'severity' => 'HIGH']
$withoutNulls = $mapper->toArray(source: $draft, configuration: Configuration::default()->omittingNulls());

Mapper::create()->withNaming(namingStrategy: SnakeCase::create());