horizom/dto

Data Transfer Objects for all PHP applications

Maintainers

Package info

github.com/horizom/dto

pkg:composer/horizom/dto

Statistics

Installs: 1 607

Dependents: 0

Suggesters: 0

Stars: 2

Open Issues: 0

5.2.0 2026-04-21 11:27 UTC

This package is auto-updated.

Last update: 2026-04-21 11:28:34 UTC


README

Horizom DTO

Data Transfer Objects for all PHP applications.

Total Downloads Latest Stable Version License

Data Transfer Objects (DTOs) are objects that are used to transfer data between systems. DTOs are typically used in applications to provide a simple, consistent format for transferring data between different parts of the application, such as between the user interface and the business logic.

Installation

composer require horizom/dto

Usage

Defining DTO Properties

use Horizom\DTO\DTO;

class UserDTO extends DTO
{
    public string $name;

    public string $email;

    public string $password;
}

Creating DTO Instances

You can create a DTO instance on many ways:

From array

$dto = new UserDTO([
    'name' => 'John Doe',
    'email' => 'john.doe@example.com',
    'password' => 's3CreT!@1a2B'
]);

You can also use the fromArray static method:

$dto = UserDTO::fromArray([
    'name' => 'John Doe',
    'email' => 'john.doe@example.com',
    'password' => 's3CreT!@1a2B'
]);

From JSON strings

$dto = UserDTO::fromJson('{"name": "John Doe", "email": "john.doe@example.com", "password": "s3CreT!@1a2B"}');

Accessing DTO Data

After you create your DTO instance, you can access any properties like an object:

$dto = new UserDTO([
    'name' => 'John Doe',
    'email' => 'john.doe@example.com',
    'password' => 's3CreT!@1a2B'
]);

$dto->name; // 'John Doe'
$dto->email; // 'john.doe@example.com'
$dto->password; // 's3CreT!@1a2B'

Casting DTO Properties

You can cast your DTO properties to some types:

use App\Enums\UserRole;
use Carbon\Carbon;
use Horizom\DTO\DTO;
use DateTimeImmutable;
use Horizom\DTO\Casting\ArrayCast;
use Horizom\DTO\Casting\EnumCast;

class UserDTO extends DTO
{
    public string $id;

    public string $name;

    public string $email;

    public string $password;

    public Carbon $created_at;

    public DateTimeImmutable $updated_at;

    public array $roles;

    protected function casts()
    {
        return [
            'id' => 'integer',
            'name' => 'string',
            'email' => 'string',
            'password' => 'string',
            'created_at' => Carbon::class,
            'updated_at' => DateTimeImmutable::class,
            'admin_role' => UserRole::class,
            'roles' => new ArrayCast(new EnumCast(UserRole::class)),
        ];
    }
}

Defining Default Values

Sometimes we can have properties that are optional and that can have default values. You can define the default values for your DTO properties in the defaults function:

use Horizom\DTO\DTO;
use Illuminate\Support\Str;

class UserDTO extends DTO
{
    // ...

    protected function defaults()
    {
        return [
            'username' => Str::slug($this->name),
        ];
    }
}

With the DTO definition above you could run:

$dto = new UserDTO([
    'name' => 'John Doe',
    'email' => 'john.doe@example.com',
    'password' => 's3CreT!@1a2B'
]);

$dto->username; // 'john_doe'

Built-in Cast Types

The following type alias strings are available out of the box in casts():

Alias Class Description
'string' StringCast Casts to string via (string)
'integer' IntegerCast Casts to int; throws on non-numeric input
'boolean' BooleanCast Casts to bool; handles 'true', 'yes', '1', etc.
'double' FloatCast Casts to float; throws on non-numeric input
'array' ArrayCast Casts to array; accepts JSON strings
'object' ObjectCast Casts to stdClass; accepts JSON strings or arrays
'datetime' DateTimeCast Casts to DateTimeImmutable (default format Y-m-d H:i:s, UTC)

For custom formats or timezones, instantiate DateTimeCast directly:

'published_at' => new DateTimeCast('d/m/Y', 'Europe/Paris'),

Transforming DTO Data

You can convert your DTO to some formats:

To array

Cast property values are automatically reversed (uncasted) to scalars: enums → their backing value, DateTimeInterface → formatted string, nested DTOs → recursive array.

$dto = new UserDTO([
    'name' => 'John Doe',
    'email' => 'john.doe@example.com',
    'password' => 's3CreT!@1a2B',
]);

$dto->toArray();
// [
//     "name" => "John Doe",
//     "email" => "john.doe@example.com",
//     "password" => "s3CreT!@1a2B",
// ]

To JSON string

$dto->toJson();
// '{"name":"John Doe","email":"john.doe@example.com","password":"s3CreT!@1a2B"}'

DTOs can also be cast directly to string — (string) $dto calls toJson() automatically.

Inspecting DTO State

Check if any data is present

$dto->filled(); // true if at least one property is non-null

Access original raw input

$dto->getOriginal();
// Returns the unmodified array originally passed to the constructor or fill()

Re-hydrate an existing instance

$dto->fill(['name' => 'Jane Doe', 'email' => 'jane@example.com']);
// Re-populates the DTO, re-applies casts and defaults

Create Your Own Type Cast

Castable classes

You can easily create new Castable types for your project by implementing the Horizom\DTO\Contracts\CastableContract interface. This interface has a single method that must be implemented:

public function cast(string $property, mixed $value): mixed;

Let's say that you have a URLWrapper class in your project, and you want that when passing a URL into your DTO it will always return a URLWrapper instance instead of a simple string:

use Horizom\DTO\Contracts\CastableContract;

class URLCast implements CastableContract
{
    public function cast(string $property, mixed $value): URLWrapper
    {
        return new URLWrapper($value);
    }
}

Then you could apply this to your DTO:

use Horizom\DTO\DTO;

class CustomDTO extends DTO
{
    protected function casts()
    {
        return [
            'url' => new URLCast(),
        ];
    }

    protected function defaults()
    {
        return [];
    }
}

Callable casts

You can also create new Castable types for your project by using a callable/callback:

use Horizom\DTO\DTO;

class CustomDTO extends DTO
{
    protected function casts(): array
    {
        return [
            'url' => function (string $property, mixed $value) {
                return new URLWrapper($value);
            },
        ];
    }

    protected function defaults(): array
    {
        return [];
    }
}

Or you can use a static method:

use Horizom\DTO\Casting\Cast;
use Horizom\DTO\DTO;

class CustomDTO extends DTO
{
    protected function casts()
    {
        return [
            'url' => Cast::make(
                function (string $property, mixed $value) {
                    return new URLWrapper($value);
                },
                function (string $property, URLWrapper $value) {
                    return $value->toString();
                }
            )
        ];
    }

    protected function defaults()
    {
        return [];
    }
}

Case of possibility of extending with Laravel

You can extend the DTO class to create your own DTO class with some custom methods:

use App\Http\Resources\UserResource;
use Horizom\DTO\DTO;
use Illuminate\Database\Eloquent\Model;

class UserDTO extends DTO
{
    public int $id;

    public string $name;

    public string $email;

    public string $password;

    public Carbon $created_at;

    public CarbonImmutable $updated_at;

    public DateTimeImmutable $verified_at;

    public static function fromModel(Model $model) {
        return new self($model->toArray());
    }

    public function toModel() {
        return new Model($this->toArray());
    }

    public function toResource() {
        return new UserResource($this->toArray());
    }

    protected function casts()
    {
        return [
            'id' => 'integer',
            'name' => 'string',
            'email' => 'string',
            'password' => 'string',
            'created_at' => Carbon::class,
            'updated_at' => CarbonImmutable::class,
            'verified_at' => DateTimeImmutable::class,
        ];
    }
}

License

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