petrenkoanton/php-dto

Custom dto implementation

v2.0.0 2024-02-22 13:37 UTC

This package is auto-updated.

Last update: 2024-04-22 14:26:47 UTC


README

PHP Version Latest Version on Packagist Total Downloads License

PHP Composer Coverage Status type-coverage psalm-level Build Status

Installation | Functionality | Usage | For developers | License | Related projects

Installation

Requirements

  • php 8.1 or higher

Composer

composer require petrenkoanton/php-dto

Functionality

Public methods

Dto

All getters are provided using the __call() magic method.

Method Exception
__construct(array $data) DtoException
__call(string $name, array $arguments): mixed DtoException
toArray(): array -

DtoCollection

Method Exception
__construct(Dto ...$items) DtoException
add(Collectable $item): void DtoException

Parent Collection

github.com/PetrenkoAnton/php-collection

Method Exception
filter(callable $callback): Collection -
getItems(): array -
getItem(int $key): Collectable CollectionException
first(): Collectable CollectionException
count(): int -

Exceptions

Main library exception is DtoException.

There are 3 groups of exceptions: InitDtoException, SetupDtoException and HandleDtoException

Code Message pattern Exception Group
101 Dto: %s | Property: %s | Err: Missed property type declaration NoTypeDeclarationException InitDtoException
102 Dto: %s | Property: %s | Err: Unsupported mixed property type declaration MixedDeclarationException InitDtoException
103 Dto: %s | Property: %s | Err: Unsupported object property type declaration ObjectDeclarationException InitDtoException
104 Dto: %s | Property: %s | Err: Class must implement DtoInterface NotDtoClassDeclarationException InitDtoException
105 Dto: %s | Property: %s | Err: No backing value for enum EnumNoBackingValueException InitDtoException
106 DtoCollection: %s | Err: Invalid constructor declaration DtoCollectionConstructorException InitDtoException
201 DtoCollection: %s | Expected Dto: %s | Given Dto: %s AddDtoException SetupDtoException
202 Dto: %s | Property: %s | Err: No data InputDataException SetupDtoException
203 Dto: %s | Property: %s | Expected type: %s | Given type: %s | Value: %s SetValueException SetupDtoException
204 Dto: %s | Property: %s | Enum: %s | Expected values: %s | Given type: %s | Value: %s SetValueEnumException SetupDtoException
301 Dto: %s | %s GetValueException HandleDtoException

Usage

Initialization

  • Your dto class must extends Dto\Dto abstract class.
  • You need to declare available protected properties.

Important! Getter will be with the prefix is* if property is a bool type.

Simple DTO

<?php

declare(strict_types=1);

use Dto\Dto;

/**
 * @method int getPrice()
 * @method string getType()
 * @method array getInfo()
 * @method bool isAvailable()
 */
class ProductDto extends Dto
{
    protected int $price;
    protected string $type;
    protected array $info;
    protected bool $available;
}

// Array or instance of Arrayable interface
$info = [
    'key' => 'value',
];

$data = [
    'price' => 999,
    'type' => 'ticket',
    'info' => $info,
    'available' => true,
];

$dto = new ProductDto($data);

$price = $dto->getPrice(); // 999
$type = $dto->getType(); // 'ticket'
$info = $dto->getInfo(); // ['key' => 'value']
$available = $dto->isAvailable(); // true

Nested DTO (with Collection and Enum)

<?php

declare(strict_types=1);

use Collection\Arrayable;
use Dto\Dto;
use Dto\DtoCollection;

/**
 * @method string getName()
 * @method int getAge()
*/
class PersonDto extends Dto
{
    protected string $name;
    protected int $age;
}

class PersonDtoCollection extends DtoCollection
{
    public function __construct(PersonDto ...$items)
    {
        parent::__construct(...$items);
    }
}

/**
 * @method int getPrice()
 * @method string getType()
 * @method array getInfo()
 * @method bool isAvailable()
 */
class ProductDto extends Dto
{
    protected int $price;
    protected string $type;
    protected array $info;
    protected bool $available;
}

enum ColorEnum: string
{
    case Red = 'red';
    case Black = 'black';
    case White = 'white';
}

/**
 * @method PersonDtoCollection getPersons()
 * @method ProductDto getProduct()
 * @method ColorEnum getColor()
 */
class NestedDto extends Dto
{
    protected PersonDtoCollection $persons;
    protected ProductDto $product;
    protected ColorEnum $color;
}

class NestedDtoFactory
{
    public function create(array $data): NestedDto
    {
        return new NestedDto($data);
    }
}

class InfoArrayable implements Arrayable
{
    public function toArray(): array
    {
        return [
            'key' => 'value',
        ];
    }
}

$data = [
    'persons' => [
        [
            'name' => 'Alice',
            'age' => 25,
        ],
        [
            'name' => 'Bob',
            'age' => 30,
        ],
    ],
    'product' => [
        'price' => 999,
        'type' => 'ticket',
        'info' => new InfoArrayable(),
        'available' => true,
    ],
    'color' => 'red',
];

$nestedDto = (new NestedDtoFactory())->create($data);

$personsCount = $nestedDto->getPersons()->count() // 2

$aliceDto = $nestedDto->getPersons()->first();
$aliceName = $aliceDto->getName(); // 'Alice'
$aliceAge = $aliceDto->getAge(); // 25

$bobDto = $nestedDto->getPersons()->filter(
    fn (PersonDto $personDto) => $personDto->getName() === 'Bob'
)->first();
$bobName = $bobDto->getName(); // 'Bob'
$bobAge = $bobDto->getAge(); // '30'

$productDto = $nestedDto->getProduct();
$productPrice = $productDto->getPrice(); // 999
$productInfo = $productDto->getInfo(); // ['key' => 'value']

$color = $nestedDto->getColor(); // ColorEnum::Red
$colorValue = $colorEnum->value; // 'red'

Return DTO as array

<?php

declare(strict_types=1);

use Dto\Dto;
use Dto\KeyCase;

/**
 * @method int getActualNumber()
 * @method string getProviderName()
 */
class SnakeCaseDto extends Dto
{
    protected int $actualNumber;
    protected string $providerName;
}

$data = [
    'actualNumber' => 5,
    'providerName' => 'Main Provider',
];

$dto = new SnakeCaseDto($data);

$array = $dto->toArray(); // ['actualNumber' => 5, 'providerName' => 'Main Provider']
$arrayWithSnakeCaseKeys = $dto->toArray(KeyCase::SNAKE_CASE); // ['actual_number' => 5, 'provider_name' => 'Main Provider']

For developers

Requirements

Utils:

Setup

Initialize

Create ./docker/.env

make init 

Build container with the different php version

php 8.1

make up81 

php 8.2

make up82

php 8.3

make up83

Also you need to run this command before build container with another php version. It will remove network and previously created container.

make down

Other commands

Go inside of the container

make inside

Check php version

make php-v

Check package version

make v

Run tests and linters

Run PHPUnit tests with code coverage

make test-c 

Run Psalm

make psalm

Run PHP_CodeSniffer

make phpcs

Or by all-in-one command from the inside of the container

composer check-all

License

The php-dto library is open-sourced software licensed under the MIT license.

Related projects