taboritis/dto-wizard-php

Data Transfer Objects with nested structures in PHP

0.1.1 2023-11-12 11:34 UTC

This package is auto-updated.

Last update: 2024-05-13 23:32:35 UTC


README

What problem does it solve?

Sometimes it may be necessary to convert a complex multi-layered array into an object-oriented format that preserves the original structure.

To achieve this result DTO-Wizard provides a straightforward API that eliminates the need for repetitive iterations and transformations.

Basic usage

class User
{
    public string $name;
    public int $age;
    public int $score;
}

$rawData = ['name'=> 'John', 'age'=> 25];

$user = \Taboritis\DTO\Factory::create(User::class, $rawData);

$user->name     // John
$user->age      // 25
$user->score    // Error - property must be initialized

Readonly properties

It is strongly recommended to use readonly properties as often as possible. This will have the same effect as unchanged data.

class User
{
    public readonly string $name;
}

$user = User::create(['name'=>'John']);
$user->name = 'Mary'; // Error: Cannot initialize readonly property 

Private properties

Although setting private properties is not recommended in DTO, it can be done using DtoWizard/Factory. However, it is important to remember that in such cases, you must also create a getter that allows access to the given property. Of course, in this situation, you can benefit from the advantages of using a getter, such as its ability to handle properties that are unset or nullable.

class User
{
    private readonly string $name;
    
    public function getName(): string
    {
        return $this->name ?? 'default';
    }
}

$user = User::create(['name'=>'John'])
$user->name = 'Mary'; // Error: Cannot initialize readonly property 

Default values

Default properties can be set in 3 ways:

  • in the property itself (warning: readonly properties cannot have default value)
  • in the constructor
  • in the getter.

It's entirely up to you to decide whether and how to utilize this feature.

Casting values by examples

In simple cases, PHP's type juggling mechanism casts assigned property values to the necessary type. https://www.php.net/manual/en/language.types.type-juggling.php

class User
{
    public readonly int $age;
}

$user = User::create(['age' => '23']);  // '23' (string)
$user->age                              // 23 (integer)

Boolean

class User
{
    public readonly bool $active;
}

$user = \Taboritis\DTO\Factory::create(User::class, ['active' => 0]);
$user->active // false (boolean)

Strings

class User
{
    public readonly bool $active;
}

$user = \Taboritis\DTO\Factory::create(User::class, ['name' => 11]);
$user->name // '11' (string)

Numbers

class User
{
    public readonly int $age;
}

$user = \Taboritis\DTO\Factory::create(User::class, ['age' => '25.7']);
$user->age // 25 (int, but with warning about deprecation)

Arrays

// TODO

DateTime

class User
{
    public readonly DateTimeImmutable $verifiedAt;
}

$user = \Taboritis\DTO\Factory::create(User::class, ['verifiedAt'=> '2020-10-10 00:00:00']);
$user->verifiedAt // instance of DateTimeImmutable

Objects

class Address
{
    public readonly string $street;
    public readonly string $number;
}

class User
{
    public readonly Address $address;
}

$user = \Taboritis\DTO\Factory::create(User::class, [
    'name' => 'Hudson (landlady)',
    'title' => 'Mrs.'
    'address' => [
        'street' => 'Baker Street',
        'number' => '221B'
    ]
]);

$user->address // instance of Department
$user->address->street // Baker Street 

Collections

// TODO: must implement Traversable