bfg/dto

The data transfer object pattern for Laravel

v1.1.2 2024-12-19 19:17 UTC

This package is auto-updated.

Last update: 2024-12-19 19:17:51 UTC


README

Latest Stable Version License Downloads

The data transfer object pattern

Installation

composer require bfg/dto

after install your can create a new dto by use this command

php artisan make:dto UserDto

Usage

Introduction

Variety of constructs for creating DTOs

The package provides a variety of methods for creating DTOs (Data Transfer Objects), which significantly improves flexibility. Methods such as fromArray, fromModel, fromRequest, fromJson, fromSerialize and others allow you to conveniently create DTOs from different data sources.

Support for nested DTOs

The package supports nested DTOs with typing, which makes it easy to work with complex data, such as addresses or comments in the user example. This simplifies data processing in cases with dependencies between objects.

Rich customization of properties

Support for data casting, such as datetime, bool, as well as property extension through methods and attributes, such as DtoName, DtoFromConfig, DtoFromRequest are useful and powerful tools that make DTOs even more versatile.

Diving into events

The ability to handle various events (creating, created, mutating and others) provides greater flexibility in managing the state of the DTO. This can be useful for implementing validation logic or transforming data before or after it is used.

DTO Collections Support

Including support for DTO collections with methods for saving them to the database or models is a great addition that makes it easier to work with multiple objects.

Additional Features

Convenient helpers for working with DTOs, such as validate, restore, cache, as well as methods for working with metadata and logs, make this package a great tool for organizing the data structure in large projects.

Ease of Use

The ease of creating DTOs via the php artisan make:dto command and obvious typing of properties make the package developer-friendly, ensuring both clean code and good integration with the Laravel framework.

Overall, this package looks like a powerful tool for structuring data in Laravel, suitable for use in projects with large amounts of data and complex models. Everything looks logical and flexible, which allows you to quickly adapt it to various needs.

First steps

Before you can use the DTO, you need to create a new class that extends the Bfg\Dto\Dto class. All you dto properties must be public and have a type hint. Also you must use the constructor for the DTO. And you can use the dependency injection in the constructor.

use Bfg\Dto\Dto;

class UserDto extends Dto
{
    protected array $hidden = [
        'password'
    ];
    
    public function __construct(
        public string $name,
        public string $email,
        public ?string $password,
    ) {}
}

After creating the DTO, you can use it in your code.

$dto = UserDto::fromArray([
    'name' => 'John Doe',
    'email' => 'test@gmail.com',
    'password' => '123456'
]);
// Or
$dto = UserDto::new(
    name: 'John Doe',
    email: 'test@gmail.com',
    password: '123456'
);

echo $dto->name; // John Doe

Constructors

fromEmpty

You can create a new DTO with empty properties.

$dto = UserDto::fromEmpty();

fromArray

You can create a new DTO from an array.

$dto = UserDto::fromArray([
    'name' => 'John Doe',
    'email' => 'test@gmail.com',
]);

fromAnything

You can create a new DTO from anything.

$dto = UserDto::fromAnything(['name' => 'John Doe', 'email' => 'test@gmail.com']);
$dto = UserDto::fromAnything([
    ['name' => 'John Doe', 'email' => 'test@gmail.com'],
    ['name' => 'John Doe', 'email' => 'test@gmail.com'],
]);
$dto = UserDto::fromAnything(\Illuminate\Foundation\Http\FormRequest::class);
$dto = UserDto::fromAnything('{"name":"John Doe","email":"test@gmail.com"}');
$dto = UserDto::fromAnything('C:8:"UserDto":23:{a:2:{s:4:"name";s:8:"John Doe";s:5:"email";s:13:"test@gmail.com";}}');
$dto = UserDto::fromAnything(User::find(1));

fromGet

You can create a new DTO from the get request.

$dto = UserDto::fromGet(string $url, array|string|null $query = null, array $headers = []);

UserDto::fromGet('https://test.dev', ['name' => 'John Doe', 'email' => 'test@gmail.com']);

fromPost

You can create a new DTO from the post request.

$dto = UserDto::fromPost(string $url, array $data = [], array $headers = []);

UserDto::fromPost('https://test.dev', ['name' => 'John Doe', 'email' => 'test@gmail.com']);

fromHttp

You can create a new DTO from the http request.

$dto = UserDto::fromHttp(string $method, string $url, array|string|null $data = [], array $headers = []): DtoCollection|static|null;

UserDto::fromHttp('get', 'https://test.dev', ['name' => 'John Doe', 'email' => 'test@gmail.com']);

fromRequest

You can create a new DTO from the request.

$dto = UserDto::fromRequest(\Illuminate\Foundation\Http\FormRequest::class);

fromJson

You can create a new DTO from json.

$dto = UserDto::fromJson('{"name":"John Doe","email":"test@gmail.com"}');

fromSerialize

You can create a new DTO from serialize.

$dto = UserDto::fromSerialize('C:8:"UserDto":23:{a:2:{s:4:"name";s:8:"John Doe";s:5:"email";s:13:"test@gmail.com";}}');

fromModel

You can create a new DTO from model.

$dto = UserDto::fromModel(User::find(1));

fromStaticCache

You can create a new DTO from static cache.

$dto = UserDto::fromStaticCache('user', function () {
    return UserDto::fromArray([
        'name' => 'John Doe',
        'email' => 'test@gmail.com',
    ]);
});

fromCollection

You can create a new DTO from collection.

$collection = UserDto::fromCollection([
    ['name' => 'John Doe', 'email' => 'test@gmail.com'],
    ['name' => 'Sam Doe', 'email' => 'test2@gmail.com'],
]);

fromCache

You can create a new DTO from cache.

// You can cache dto before
$dto->cache();

$dto = UserDto::fromCache(function () {
    // If cache not found
    return UserDto::fromArray([
        'name' => 'John Doe',
        'email' => 'test@gmail.com',
    ]);
});

fromSource

You can create a new DTO from source.

use Bfg\Dto\Dto;

class UserDto extends Dto
{
    public function __construct(
        public string $name,
        public string $email,
        public ?string $password,
    ) {}
    
    public static function sourceV1(...$arguments): array {
    
        // Do something
    
        return [
            'name' => 'John Doe',
            'email' => 'test@gmail.com',
            'password' => '123456',
        ];
    }
}

And after that you can create a new DTO from source.

$dto = UserDto::fromSource('v1', ...$arguments);

Nested DTO calls

For nested DTO calls, you can use type hinting.

use Bfg\Dto\Dto;

class AddressDto extends Dto
{
    public function __construct(
        public string $city,
        public string $street,
    ) {}
}

class CommentDto extends Dto
{
    public function __construct(
        public string $message,
    ) {}
}

class UserDto extends Dto
{
    protected array $hidden = [
        'password'
    ];
        
    public function __construct(
        public string $name,
        public string $email,
        public ?string $password,
        public AddressDto $address,
        public CommentDto|array $comments,
    ) {}
}

Now you can use nested DTOs.

$dto = UserDto::fromArray([
    'name' => 'John Doe',
    'email' => 'test@gmail.com',
    'password' => '123456',
    'address' => [
        'city' => 'New York',
        'street' => 'Wall Street',
    ],
    'comments' => [
        ['message' => 'The first comment'],
        ['message' => 'The second comment'],
    ]
]);

echo $dto->address->city; // New York
// And
foreach ($dto->comments as $comment) {
    echo $comment->message;
}

Binding

You can use binding for the DTO.

Binding to the model

You can bind the DTO property to the model.

use Bfg\Dto\Dto;
use App\Models\User;
use Bfg\Dto\Attributes\DtoName;

class UserDto extends Dto
{
    public function __construct(
        public string $name,
        public string $email,
        #[DtoName('user_id')] 
        public ?User $user,
    ) {}
}

$dto = UserDto::fromArray([
    'name' => 'John Doe',
    'email' => 'test@gmail.com',
    'user_id' => 1,
    // Or 
    'user' => 1, 
]);

dump($dto->user); // User model
// In Array you will get the id of the model
dump($dto->toArray()); // ['name' => 'John Doe', 'email' => 'test@gmail.com', 'user_id' => 1]

If model User with id 1 exists, it will be bound to the DTO property. If not, the property will be null.

Binding to the Carbon

You can bind the DTO property to the Carbon. Default format for the Carbon is Y-m-d H:i:s. You can change the format using the $dateFormat property.

use Bfg\Dto\Dto;

class UserDto extends Dto
{
    public function __construct(
        public string $name,
        public string $email,
        public \Carbon\Carbon $created_at,
    ) {}
}

$dto = UserDto::fromArray([
    'name' => 'John Doe',
    'email' => 'test@gmail.com',
    'created_at' => '2025-01-01 00:00:00',
]);

dump($dto->created_at); // Carbon object
// In Array you will get the date in the format `Y-m-d H:i:s`
dump($dto->toArray()); // ['name' => 'John Doe', 'email' => 'test@gmail.com', 'created_at' => '2025-01-01 00:00:00']

Property extends

You can use property extends for extending the DTO.

use Bfg\Dto\Dto;

class UserDto extends Dto
{
    public function __construct(
        public string $email,
        public ?string $password,
    ) {}
}
class SpecialUserDto extends UserDto
{
    protected static array $extends = [
        'name' => 'string|null',
        // Or
        'name' => ['string', 'null'],
    ];
}

This way you will expand the properties for DTO.

All properties have identical behavior to properties in the property promotion constructor.

Property casting

You can use property casting like in Laravel models.

use Bfg\Dto\Dto;

class UserDto extends Dto
{
    protected static array $casts = [
        'is_admin' => 'bool',
        'created_at' => 'datetime',
    ];
        
    public function __construct(
        public string $name,
        public string $email,
        public ?string $password,
        public bool $is_admin,
        public \Carbon\Carbon $created_at,
    ) {}
}

Also, this casting support class casting. You can create a new class casting using the artisan command:

php artisan make:dto-cast UserNameCast

After creating the class casting, you can use it in the DTO.

use Bfg\Dto\Dto;

class UserDto extends Dto
{
    protected static array $casts = [
        'is_admin' => 'bool',
        'created_at' => 'datetime',
        'name' => UserNameCast::class,
    ];
}

And casting support enum casting. You can create a new enum casting using the artisan command:

php artisan make:enum UserStatusEnum

After creating the enum casting, you can use it in the DTO.

use Bfg\Dto\Dto;

class UserDto extends Dto
{
    protected static array $casts = [
        'is_admin' => 'bool',
        'created_at' => 'datetime',
        'name' => UserNameCast::class,
        'status' => UserStatusEnum::class,
    ];
}

Property hidden

You can use property hidden like in Laravel models.

use Bfg\Dto\Dto;

class UserDto extends Dto
{
    protected array $hidden = [
        'password'
    ];
            
    public function __construct(
        public string $name,
        public string $email,
        public ?string $password,
    ) {}
}

$dto = UserDto::fromArray([
    'name' => 'John Doe',
    'email' => 'test@gmail.com',
    'password' => '123456',
]);

echo $dto->toArray(); // ['name' => 'John Doe', 'email' => 'test@gmail.com']

Property rules

You can use property rules like in Laravel requests.

use Bfg\Dto\Dto;

class UserDto extends Dto
{
    protected static array $rules = [
        'name' => 'required|string',
        'email' => 'required|email',
        'password' => 'required|string|min:6',
    ];
    
    protected static array $ruleMessages = [
        'name.required' => 'The name field is required.',
        'email.required' => 'The email field is required.',
        'password.required' => 'The password field is required.',    
    ];
                
    public function __construct(
        public string $name,
        public string $email,
        public ?string $password,
    ) {}
}

$dto = UserDto::fromArray([
    'name' => 'John Doe',
]); // Throws an exception

Property encrypted

You can use property encrypted for getting encrypted data.

use Bfg\Dto\Dto;

class UserDto extends Dto
{
    protected array $encrypted = [
        'password'
    ];
                
    public function __construct(
        public string $name,
        public string $email,
        public string $password, // Data will be decrypted
    ) {}
}

$dto = UserDto::fromArray([
    'name' => 'John Doe',
    'email' => 'test@gmail.com',
    'password' => \Illuminate\Support\Facades\Crypt::encrypt('123456'),
]);

echo $dto->password; // You will get decrypted password

dump($dto->toArray()); // ['name' => 'John Doe', 'email' => 'test@gmail.com', 'password' => 'encrypted data']

Property mutations

You can use property mutations like in Laravel models.

use Bfg\Dto\Dto;

class UserDto extends Dto
{            
    public function __construct(
        public string $name,
        public string $email,
        public ?string $password,
    ) {}
    
    protected function fromArrayName(string $name): string
    {
        return ucwords($name);
    }

    protected function toArrayName(string $name): string
    {
        return strtolower($name);
    }
}
  • fromArrayName method will be called when you create a new DTO data from an array.
  • toArrayName method will be called when you convert DTO data to an array.

This method gets the property value and must return the new value.

Computed properties

You can use computed properties.

use Bfg\Dto\Dto;

class UserDto extends Dto
{            
    public function __construct(
        public string $name,
        public string $lastName,
        public string $email,
        public ?string $password,
    ) {}
        
    public function fullName(): string
    {
        return $this->name . ' ' . $this->lastName;
    }
}

Now you can use computed properties.

$dto = UserDto::fromArray([
    'name' => 'John',
    'lastName' => 'Doe',
    'email' => 'test@gmail.com',
    'password' => '123456'
]);

echo $dto->fullName; // John Doe

Lazy properties

You can use lazy like properties any property or computed property. If you add the prefix "lazy" before the name of a property or a computed property, you will get access to lazy execution. When you request a property, the value that this property receives is cached and all subsequent times the result for the lazy property is taken from the cache and if in DTO the property changes, then in Lazy it will remain the same since it is taken from the cache.

use Bfg\Dto\Dto;

class UserDto extends Dto
{            
    public function __construct(
        public string $name,
        public string $lastName,
        public string $email,
        public ?string $password,
    ) {}
        
    public function fullName(): string
    {
        return $this->name . ' ' . $this->lastName;
    }
}

$dto = UserDto::fromArray([
    'name' => 'John',
    'lastName' => 'Doe',
    'email' => 'test@gmail.com',
    'password' => '123456'
]);

echo $dto->lazyEmail; // test@gmail.com
echo $dto->lazyFullName; // John Doe, and it put in the cache
$dto->set('name', 'Sam');
echo $dto->lazyFullName; // John Doe, because it is taken from the cache

Method property access

You can use the method property access.

use Bfg\Dto\Dto;

class UserDto extends Dto
{
    public function __construct(
        public string $name,
        public string $email,
        public ?string $password,
    ) {}
}

$dto = UserDto::fromArray([
    'name' => 'John Doe',
    'email' => 'test@gmail.com',
    'password' => '123456',
]);

echo $dto->name(); // John Doe
// The same as
echo $dto->get('name'); // John Doe

Method default for field

You can use the default method for setting the default value for the field.

use Bfg\Dto\Dto;

class UserDto extends Dto
{
    public function __construct(
        public string $name,
        public string $email,
        public ?string $password,
    ) {}
    
    public static function defaultName()
    {
        return 'Jon';
    }
}

After that, you can escape the name field when creating a DTO.

$dto = UserDto::fromArray([
    'email' => 'test@gmail.com',
    'password' => '123456',
]);

echo $dto->name; // Jon

Collection hinting

You can use collection hinting.

use Bfg\Dto\Dto;

class UserDto extends Dto
{            
    public function __construct(
        public string $name,
        public string $email,
        public ?string $password,
        public AddressDto|array $address,
        // Or
        public AddressDto|\Illuminate\Support\Collection $address,
    ) {}
}

$dto = UserDto::fromArray([
    'name' => 'John Doe',
    'email' => 'test@gmail.com',
    'password' => '123456',
    'address' => [
        ['city' => 'New York', 'street' => 'Wall Street'],
        ['city' => 'Los Angeles', 'street' => 'Hollywood Street'],    
    ]
]);

foreach ($dto->address as $address) {

    echo $address->city;
}

Property by method with

You can use the with... method for adding data to the DTO array.

use Bfg\Dto\Dto;

class UserDto extends Dto
{            
    public function __construct(
        public string $name,
        public string $lastName,
        public string $email,
        public ?string $password,
    ) {}
        
    public function withFullName(): string
    {
        return $this->name . ' ' . $this->lastName;
    }
}

$dto = UserDto::fromArray([
    'name' => 'John',
    'lastName' => 'Doe',
    'email' => 'test@gmail.com',
    'password' => '123456'
]);

dump($dto->toArray()); // ['name' => 'John', 'lastName' => 'Doe', 'email' => 'test@gmail.com', 'password' => '123456', 'fullName' => 'John Doe']

Property logsEnabled

You can use property logsEnabled for logging data in the DTO memory.

use Bfg\Dto\Dto;

class UserDto extends Dto
{
    protected static bool $logsEnabled = true;
    
    public function __construct(
        public string $name,
        public string $email,
        public ?string $password,
    ) {}
}

$dto = UserDto::fromArray([
    'name' => 'John Doe',
    'email' => 'test@gmail.com',
    'password' => '123456',
]);

$dto->set('name', 'Sam Doe');

dump($dto->logs()); // You will get logs DTO

You can write your own logs to the DTO memory using the log method.

$dto->log(string $message, array $context = []);

Meta

You can use meta.

$dto = UserDto::fromArray([
    'name' => 'John Doe',
    'email' => 'test@gmail.com',
    'password' => '123456',
]);

$dto->setMeta(['key' => 'value']);
$dto->setMeta(['key2' => 'value2']);
echo $dto->getMeta('key'); // value
echo $dto->getMeta('key2'); // value2
// You can get all meta
dump($dto->getMeta()); // ['key' => 'value', 'key2' => 'value2']
// You can remove meta
$dto->unsetMeta('key');

Attributes

You can use attributes.

DtoName

You can use the DtoName attribute to add the name of the DTO.

use Bfg\Dto\Dto;

class UserDto extends Dto
{            
    public function __construct(
        #[DtoName('user')] 
        public string $name,
        public string $email,
        public ?string $password,
    ) {}
}

$dto = UserDto::fromArray([
    'user' => 'John Doe',
    'email' => 'test@gmail.com',
    'password' => '123456'
]);
// Or
$dto = UserDto::fromArray([
    'name' => 'John Doe',
    'email' => 'test@gmail.com',
    'password' => '123456'
]);

echo $dto->name; // John Doe

Here we have added an additional field name to the "name" field and we can now use 2 fields to insert data.

For property extends:

use Bfg\Dto\Dto;

class UserDto extends Dto
{            
    #[DtoName('user', 'name')]
    protected static array $extends = [
        'name' => 'string',
    ];

    public function __construct(
        public string $email,
        public ?string $password,
    ) {}
}

Since we cannot write an attribute to a separate element of the array, we need to specify the name of the field to which we need to assign an additional name as the second parameter.

DtoFromConfig

You can use the DtoFromConfig attribute to add the property value from the config.

use Bfg\Dto\Dto;

class UserDto extends Dto
{            
    public function __construct(
        public string $name,
        public string $email,
        public ?string $password,
        #[DtoFromConfig('app.name')]
        public string $appName,
    ) {}
}

$dto = UserDto::fromArray([
    'name' => 'John Doe',
    'email' => 'test@gmail.com',
    'password' => '123456'
]);

echo $dto->appName; // Laravel

DtoFromRequest

You can use the DtoFromRequest attribute to add the property value from the request.

use Bfg\Dto\Dto;

class UserDto extends Dto
{            
    public function __construct(
        public string $name,
        public string $email,
        public ?string $password,
        #[DtoFromRequest]
        public string $id,
    ) {}
}

// https://test.dev/?id=100

$dto = UserDto::fromArray([
    'name' => 'John Doe',
    'email' => 'test@gmail.com',
    'password' => '123456'
]);

echo $dto->id; // 100

DtoFromRoute

You can use the DtoFromRoute attribute to add the property value from the route.

use Bfg\Dto\Dto;

class UserDto extends Dto
{            
    public function __construct(
        public string $name,
        public string $email,
        public ?string $password,
        #[DtoFromRoute]
        public string $id,
    ) {}
}

// Route::get('/{id}', function ($id) {});

$dto = UserDto::fromArray([
    'name' => 'John Doe',
    'email' => 'test@gmail.com',
    'password' => '123456'
]);

echo $dto->id; // 100

DtoFromCache

You can use the DtoFromCache attribute to add the property value from the cache.

use Bfg\Dto\Dto;

class UserDto extends Dto
{            
    public function __construct(
        public string $name,
        public string $email,
        public ?string $password,
        
        #[DtoFromCache('user')]
        public string $userFromCache,
        // Or
        #[DtoFromCache]
        public string $user,
    ) {}
}

Cache::put('user', 'John Doe', 60);

$dto = UserDto::fromArray([
    'name' => 'John Doe',
    'email' => 'test@gmail.com',
    'password' => '123456'
]);

echo $dto->user; // John Doe

DtoToResource

You can use the DtoToResource attribute to convert the property for adding to the array. Create a new resource:

php artisan make:resource UserAddressResource
use Bfg\Dto\Dto;

class UserDto extends Dto
{            
    public function __construct(
        public string $name,
        public string $email,
        public ?string $password,
        #[DtoToResource(UserAddressResource::class)]
        public AddressDto $address,
    ) {}
}

$dto = UserDto::fromArray([
    'name' => 'John Doe',
    'email' => 'test@gmail.com',
    'password' => '123456',
    'address' => [
        'city' => 'New York',
        'street' => 'Wall Street',
    ]
]);

echo $dto->toArray(); 
// ['name' => 'John Doe', 'email' => '...', 'password' => '...', 'address' => ['city' => 'New York', 'street' => 'Wall Street']]

Address will be converted to the array using the UserAddressResource resource.

Events

You can use events.

creating

You can use the creating event.

UserDto::on('creating', function (array $arguments) {
    $arguments['name'] = 'John Doe';
    return $arguments;
});

created

You can use the created event.

UserDto::on('created', function (UserDto $dto, array $arguments) {
    $dto->name = 'John Doe';
});

updating

You can use the updating event.

UserDto::on(['updating', 'name'], function (mixed $value, UserDto $dto) {
    return strtoupper($value);
});

updated

You can use the updated event.

UserDto::on(['updated', 'name'], function (UserDto $dto) {
    $dto->name = strtoupper($dto->name);
});

mutating

You can use the mutating event.

UserDto::on(['mutating', 'name'], function (mixed $value, UserDto $dto, array $arguments) {
    return strtoupper($value);
});

mutated

You can use the mutated event.

UserDto::on(['mutated', 'name'], function (mixed $value, UserDto $dto, array $arguments) {
    return strtoupper($value);
});

serialize

You can use the serialize event.

UserDto::on('serialize', function (array $arguments, UserDto $dto) {
    $arguments['name'] = strtoupper($arguments['name']);
    return $arguments;
});

unserialize

You can use the unserialize event.

UserDto::on('unserialize', function (array $arguments, UserDto $dto) {
    $arguments['name'] = strtolower($arguments['name']);
    return $arguments;
});

clone

You can use the clone event.

UserDto::on('clone', function (array $arguments, UserDto $dto) {
    $arguments['name'] = strtolower($arguments['name']);
    return $arguments;
});

fromModel

You can use the fromModel event.

UserDto::on('fromModel', function (UserDto $dto, array $arguments) {
    // You can change the DTO data or something else
});

fromEmpty

You can use the fromEmpty event.

UserDto::on('fromEmpty', function (UserDto $dto, array $arguments) {
    // You can change the DTO data or something else
});

fromArray

You can use the fromArray event.

UserDto::on('fromArray', function (UserDto $dto, array $arguments) {
    // You can change the DTO data or something else
});

fromRequest

You can use the fromRequest event.

UserDto::on('fromRequest', function (UserDto $dto, array $arguments) {
    // You can change the DTO data or something else
});

fromJson

You can use the fromJson event.

UserDto::on('fromJson', function (UserDto $dto, array $arguments) {
    // You can change the DTO data or something else
});

fromSerialize

You can use the fromJson event.

UserDto::on('fromJson', function (UserDto $dto) {
    // You can change the DTO data or something else
});

prepareModel

You can use the prepareModel event.

UserDto::on('prepareModel', function (array $arguments) {
    $arguments['name'] = 'John Doe';
    return $arguments;
});

prepareSerialize

You can use the prepareSerialize event.

UserDto::on('prepareSerialize', function (string $serializedString) {
    // You can change the serialized string or something else
    return $serializedString;
});

prepareJson

You can use the prepareJson event.

UserDto::on('prepareJson', function (array $arguments) {
    // You can change the json array or something else
    return $arguments;
});

prepareRequest

You can use the prepareRequest event.

UserDto::on('prepareRequest', function (array $arguments) {
    // You can change the request array or something else
    return $arguments;
});

prepareArray

You can use the prepareArray event.

UserDto::on('prepareArray', function (array $arguments) {
    // You can change the array or something else
    return $arguments;
});

prepareEmpty

You can use the prepareArray event.

UserDto::on('prepareEmpty', function () {
    // You can create a new array or something else
    return [];
});

destruct

You can use the destruct event.

UserDto::on('destruct', function (UserDto $dto) {
    // You can do something with the DTO
});

Reflection

You can use reflection.

explain

You can use the explain method for getting the DTO information.

$dto->explain();

vars

You can use the vars method for getting the DTO properties.

$dto->vars();

getModifiedFields

You can use the getModifiedFields method for getting the modified fields.

$dto->getModifiedFields();

getRelationNames

You can use the getRelationNames method for getting the relation names.

$dto->getRelationNames();

getPropertyNames

You can use the getPropertyNames method for getting the property names.

$dto->getPropertyNames();

getNames

You can use the getNames method for getting the names.

$dto->getNames();

getReflection

You can use the getReflection method for getting the reflection.

$dto->getReflection();

Convert DTO to

ToArray

You can convert DTO to array.

$dto->toArray();

ToJson

You can convert DTO to json.

$dto->toJson($options = 0);

ToResponse

You can convert DTO to response.

$dto->toResponse(int $status = 200, array $headers = [], int $options = 0);

ToCollection

You can convert DTO to collection.

$dto->toCollection();

ToBase64

You can convert DTO to base64.

$dto->toBase64();

ToModel

You can convert DTO to model.

$dto->toModel(User::class);

ToSerialize

You can convert DTO to serialize.

$dto->toSerialize();

ToApi

You can convert DTO to api.

$dto->toApi(string $url, string $method = 'post', array $headers = []);

ToString

You can convert DTO to string.

$dto->toString();
//Or
echo (string) $dto;

DTO Collection

Dto collection have couple additional methods.

insertToDatabase

For save collection to database you can use saveToDatabase method.

$collection = UserDto::fromCollection([
    ['name' => 'John Doe', 'email' => 'test@gmail.com', 'password' => '123456'],
    ['name' => 'Sam Doe', 'email' => 'sam@gmail.com', 'password' => '123456'],
]);

$collection->insertToDatabase('users');

insertToModel

For save collection to model you can use saveToModel method.

$collection = UserDto::fromCollection([
    ['name' => 'John Doe', 'email' => 'test@gmail.com', 'password' => '123456'],
    ['name' => 'Sam Doe', 'email' => 'sam@gmail.com', 'password' => '123456'],
]);

$collection->insertToModel(\App\Models\User::class);

Commands

You can use commands.

Make dto

You can create a new DTO using the artisan command.

php artisan make:dto UserDto

Make dto cast

You can create a new DTO cast using the artisan command.

php artisan make:dto-cast UserNameCast

Make dto docs

You can build the DTO documentation for extended fields using the artisan command.

php artisan make:dto-docs

Add this command to the composer.json file for auto-generating DTO documentation after the composer update and composer dump-autoload command.

"scripts": {
    "post-autoload-dump": [
        "@php artisan make:dto-docs"
    ],
}

Helpers

You can use helpers.

new

You can use the new helper for creating a new DTO.

UserDto::new(
    name: 'John Doe',
    email: 'test@gmail.com',
    password: '123456',
);

version

You can use the version helper for getting the DTO version.

UserDto::version();

pipeline

You can use the pipeline helper for creating a new DTO pipeline.

$dto->pipeline([
    SomeClassForPipeline::class,
]);

cache

You can use the cache helper for caching the DTO.

$dto->cache(\DateTimeInterface|\DateInterval|int|null $ttl = null);

cacheKey

You can use the cacheKey helper for getting the cache key.

$dto->cacheKey('name');

getCachedKey

You can use the getCachedKey helper for getting the cached key.

$dto->getCachedKey('name');

cacheKeyClear

You can use the cacheKeyClear helper for clearing the cache key.

$dto->cacheKeyClear('name');

validate

You can use the validate helper for validating the DTO.

$dto->validate([
    'name' => 'required|string',
    'email' => 'required|email',
    'password' => 'required|string|min:6',
]); // bool

restore

You can use the restore helper for restoring the DTO from the original data.

$dto->restore();

originals

You can use the originals helper for getting the original data.

$dto->originals();

equals

You can use the equals helper for comparing the DTOs.

$dto->equals(UserDto::fromArray([
    'name' => 'John Doe',
    'email' => 'test@gmail.com',
    'password' => '123456',
])); // bool

fill

You can use the fill helper for filling the DTO.

$dto->fill([
    'name' => 'John Doe',
    'email' => 'test@gmail.com',
    'password' => '123456',
]);

length

You can use the length helper for getting the length of the DTO in bytes.

$dto->length();
// Or
$dto->length(\Bfg\Dto\Dto::GET_LENGTH_SERIALIZE); // You get the length of the DTO in bytes in serialized form

// And you can get the length of the DTO in bytes in json form
$dto->length(\Bfg\Dto\Dto::GET_LENGTH_JSON);

count

You can use the count helper for getting the count of the DTO.

$dto->count();

has

You can use the has helper for checking the property in the DTO.

$dto->has('name');

You also can use isset for checking the property in the DTO.

isset($dto['name']);

dataHash

You can use the dataHash helper for getting the data hash of the DTO.

$dto->dataHash();

hash

You can use the hash helper for getting the hash of the DTO.

$dto->hash();

clone

You can use the clone helper for cloning the DTO.

$dto->clone();

str

You can use the str helper for getting the string representation of the DTO.

$dto->str('name')->camel()->snake();

collect

You can use the collect helper for wrapping the DTO properties in the collection.

$dto->collect('addresses')->map(function (UserAddressDto $address) {
    return $address->city;
});

boolable

You can use the boolable helper for inverting the boolean property in the DTO.

$dto->boolable('confirmed');

toggleBool

You can use the toggleBool helper for toggling the boolean property in the DTO.

$dto->toggleBool('is_admin');

increment

You can use the increment helper for incrementing the property in the DTO.

$dto->increment('count');

decrement

You can use the decrement helper for decrementing the property in the DTO.

$dto->decrement('count');

set

You can use the set helper for setting the property in the DTO.

$dto->set('name', 'John Doe');

get

You can use the get helper for getting the property in the DTO.

$dto->get('name');

map

You can use the map helper for mapping the DTO properties.

$dto->map(function (mixed $value, string $key) {
    return strtoupper($value);
});

isEmpty

You can use the isEmpty helper for checking the DTO is property empty.

$dto->isEmpty('name');

isNotEmpty

You can use the isNotEmpty helper for checking the DTO is property not empty.

$dto->isNotEmpty('name');

isNull

You can use the isNull helper for checking the DTO is property null.

$dto->isNull('name');

isNotNull

You can use the isNotNull helper for checking the DTO is property not null.

$dto->isNotNull('name');

isCanNull

You can use the isCanNull helper for checking the DTO is property can be null.

$dto->isCanNull('name');

isTrue

You can use the isTrue helper for checking the DTO is property true.

$dto->isTrue('is_admin');

isFalse

You can use the isFalse helper for checking the DTO is property false.

$dto->isFalse('is_admin');

isBool

You can use the isBool helper for checking the DTO is property bool.

$dto->isBool('is_admin');

isEquals

You can use the isEquals helper for checking the DTO is property equals.

$dto->isEquals('name', 'John Doe');

isNotEquals

You can use the isNotEquals helper for checking the DTO is property not equals.

$dto->isNotEquals('name', 'John Doe');

isInstanceOf

You can use the isInstanceOf helper for checking the DTO is property instance of.

$dto->isInstanceOf('address', AddressDto::class);

isNotInstanceOf

You can use the isNotInstanceOf helper for checking the DTO is property not instance of.

$dto->isNotInstanceOf('address', AddressDto::class);

isString

You can use the isString helper for checking the DTO is property string.

$dto->isString('name');

isNotString

You can use the isNotString helper for checking the DTO is property not string.

$dto->isNotString('name');

isInt

You can use the isInt helper for checking the DTO is property int.

$dto->isInt('id');

isNotInt

You can use the isNotInt helper for checking the DTO is property not int.

$dto->isNotInt('id');

isFloat

You can use the isFloat helper for checking the DTO is property float.

$dto->isFloat('price');

isNotFloat

You can use the isNotFloat helper for checking the DTO is property not float.

$dto->isNotFloat('price');

isArray

You can use the isArray helper for checking the DTO is property array.

$dto->isArray('addresses');

isNotArray

You can use the isNotArray helper for checking the DTO is property not array.

$dto->isNotArray('addresses');

isObject

You can use the isObject helper for checking the DTO is property object.

$dto->isObject('address');

isNotObject

You can use the isNotObject helper for checking the DTO is property not object.

$dto->isNotObject('address');

isInstanceOfArray

You can use the isInstanceOfArray helper for checking the DTO is property instance of array.

$dto->isInstanceOfArray('addresses', AddressDto::class);

isNotInstanceOfArray

You can use the isNotInstanceOfArray helper for checking the DTO is property not instance of array.

$dto->isNotInstanceOfArray('addresses', AddressDto::class);

Customize http request

You can customize the http request.

use Bfg\Dto\Dto;

class UserDto extends Dto
{
    public function __construct(
        public string $name,
        public string $email,
        public ?string $password,
    ) {}
    
    protected static function httpClient(): PendingRequest
    {
        return Http::withoutVerifying()
            ->withoutRedirecting()
            ->withCookies(['name' => 'value'])
            ->withHeaders(['Authorization' => 'Bearer ' . auth()->user()->token]);
    }
}

Or you can customize only headers:

use Bfg\Dto\Dto;

class UserDto extends Dto
{
    public function __construct(
        public string $name,
        public string $email,
        public ?string $password,
    ) {}
        
    protected static function httpHeaders(): array
    {
        return [
            'Authorization' => 'Bearer ' . auth()->user()->token,
        ];
    }
}

Also you can customize what fromAnything can be used for the request POST by default

use Bfg\Dto\Dto;

class UserDto extends Dto
{
    protected static bool $postDefault = true;

    public function __construct(
        public string $name,
        public string $email,
        public ?string $password,
    ) {}
}

And you can customize the parameters data for the request by default

use Bfg\Dto\Dto;

class UserDto extends Dto
{
    public function __construct(
        public string $name,
        public string $email,
        public ?string $password,
    ) {}
    
    protected static function httpData(array|string|null $data): array|string|null
    {
        // Do something with data
        return $data;
    }
}

Default Laravel Support

DTO class use a famous Laravel support, such as Illuminate\Support\Traits\Conditionable, Illuminate\Support\Traits\Dumpable, Illuminate\Support\Traits\Macroable and Illuminate\Support\Traits\Tappable.

Changelog

Please see CHANGELOG for more information on what has changed recently.

Security

Please see SECURITY for more information about security.

Credits

License

The MIT License (MIT). Please see License File for more information.