Skip to content

Latest commit

History

87 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Validated DTO

中文说明

Latest Stable VersionTotal DownloadsLicense

The Data Transfer Objects with validation for Hyperf applications. Forked from laravel-validated-dto

Documentation

https://wendell-adriel.gitbook.io/laravel-validated-dto

Installation

composer require friendsofhyperf/validated-dto

Generating DTO

You can create DTO using the gen:dto command:

php bin/hyperf.php gen:dto UserDTO

The DTO are going to be created inside app/DTO.

Defining Validation Rules

You can validate data in the same way you validate Request data:

<?phpnamespaceApp\DTO;
useFriendsOfHyperf\ValidatedDTO\ValidatedDTO;
class UserDTO extends ValidatedDTO
{
protectedfunctionrules(): array
{
return [
'name' => ['required', 'string'],
'email' => ['required', 'email'],
'password' => ['required'],
];
}
}

Creating DTO instances

You can create a DTO instance on many ways:

From arrays

$dto = UserDTO::fromArray([
'name' => 'Deeka Wong',
'email' => 'deeka@example.com',
'password' => 'D3Crft!@1b2A'
]);

From JSON strings

$dto = UserDTO::fromJson('{"name": "Deeka Wong", "email": "deeka@example.com", "password": "D3Crft!@1b2A"}');

From Request objects

publicfunctionstore(RequestInterface$request): JsonResponse
{
$dto = UserDTO::fromRequest($request);
}

From Model

$user = newUser([
'name' => 'Deeka Wong',
'email' => 'deeka@example.com',
'password' => 'D3Crft!@1b2A'
]);
$dto = UserDTO::fromModel($user);

Beware that the fields in the $hidden property of the Model won't be used for the DTO.

From Artisan Commands

You have three ways of creating a DTO instance from an Artisan Command:

From the Command Arguments

<?phpuseApp\DTO\UserDTO;
useHyperf\Command\Command;
class CreateUserCommand extends Command
{
protected ?string$signature = 'create:user {name} {email} {password}';
protectedstring$description = 'Create a new User';
/** * Execute the console command. * * @return int * * @throws ValidationException */publicfunctionhandle()
{
$dto = UserDTO::fromCommandArguments($this);
}
}

From the Command Options

<?phpuseApp\DTO\UserDTO;
useHyperf\Command\Command;
class CreateUserCommand extends Command
{
protected ?string$signature = 'create:user { --name= : The user name } { --email= : The user email } { --password= : The user password }';
protectedstring$description = 'Create a new User';
/** * Execute the console command. * * @return int * @throws ValidationException */publicfunctionhandle()
{
$dto = UserDTO::fromCommandOptions($this);
}
}

From the Command Arguments and Options

<?phpuseApp\DTO\UserDTO;
useHyperf\Command\Command;
class CreateUserCommand extends Command
{
protected ?string$signature = 'create:user {name} { --email= : The user email } { --password= : The user password }';
protectedstring$description = 'Create a new User';
/** * Execute the console command. * * @return int * * @throws ValidationException */publicfunctionhandle()
{
$dto = UserDTO::fromCommand($this);
}
}

Exporting DTO Types

Export a DTO class to TypeScript with dto:export. Pass the fully qualified DTO class name and optionally write the output to a file:

php bin/hyperf.php dto:export 'App\DTO\UserDTO'
php bin/hyperf.php dto:export 'App\DTO\UserDTO' --output=resources/types/user.dto.ts --force

The --lang option accepts typescript or ts.

Accessing DTO data

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

$dto = UserDTO::fromArray([
'name' => 'Deeka Wong',
'email' => 'deeka@example.com',
'password' => 'D3Crft!@1b2A'
]);
$dto->name; // 'Deeka Wong'$dto->email; // 'deeka@example.com'$dto->password; // 'D3Crft!@1b2A'

If you pass properties that are not listed in the rules method of your DTO, this data will be ignored and won't be available in your DTO:

$dto = UserDTO::fromArray([
'name' => 'Deeka Wong',
'email' => 'deeka@example.com',
'password' => 'D3Crft!@1b2A',
'username' => 'john_doe', ]);
$dto->username; // THIS WON'T BE AVAILABLE IN YOUR DTO

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:

<?phpnamespaceApp\DTO;
useHyperf\Stringable\Str;
class UserDTO extends ValidatedDTO
{
protectedfunctionrules(): array
{
return [
'name' => ['required', 'string'],
'email' => ['required', 'email'],
'username' => ['sometimes', 'string'],
'password' => ['required'],
];
}
protectedfunctiondefaults(): array
{
return [
'username' => Str::snake($this->name),
];
}
}

With the DTO definition above you could run:

$dto = UserDTO::fromArray([
'name' => 'Deeka Wong',
'email' => 'deeka@example.com',
'password' => 'D3Crft!@1b2A'
]);
$dto->username; // 'deeka_wong'

Converting DTO data

You can convert your DTO to some formats:

To array

$dto = UserDTO::fromArray([
'name' => 'Deeka Wong',
'email' => 'deeka@example.com',
'password' => 'D3Crft!@1b2A',
]);
$dto->toArray();
// [// "name" => "Deeka Wong",// "email" => "deeka@example.com",// "password" => "D3Crft!@1b2A",// ]

To JSON string

$dto = UserDTO::fromArray([
'name' => 'Deeka Wong',
'email' => 'deeka@example.com',
'password' => 'D3Crft!@1b2A',
]);
$dto->toJson();
// '{"name":"Deeka Wong","email":"deeka@example.com","password":"D3Crft!@1b2A"}'$dto->toJson(true); // YOU CAN CALL IT LIKE THIS TO PRETTY PRINT YOUR JSON// {// "name": "Deeka Wong",// "email": "deeka@example.com",// "password": "D3Crft!@1b2A"// }

To Eloquent Model

$dto = UserDTO::fromArray([
'name' => 'Deeka Wong',
'email' => 'deeka@example.com',
'password' => 'D3Crft!@1b2A',
]);
$dto->toModel(\App\Model\User::class);
// App\Model\User {#3776// name: "Deeka Wong",// email: "deeka@example.com",// password: "D3Crft!@1b2A",// }

Customizing Error Messages, Attributes and Exceptions

You can define custom messages and attributes implementing the messages and attributes methods in your DTO class:

/** * Defines the custom messages for validator errors. */protectedfunctionmessages() {
return [];
}
/** * Defines the custom attributes for validator errors. */protectedfunctionattributes() {
return [];
}

Type Casting

You can easily cast your DTO properties by defining a casts method in your DTO:

/** * Defines the type casting for the properties of the DTO. * * @return array */protectedfunctioncasts(): array
{
return [
'name' => newStringCast(),
'age' => newIntegerCast(),
'created_at' => newCarbonImmutableCast(),
];
}

Available Types

Array

For JSON strings, it will convert into an array, for other types, it will wrap them in an array.

protectedfunctioncasts(): array
{
return [
'property' => newArrayCast(),
];
}

Boolean

For string values, this uses the filter_var function with the FILTER_VALIDATE_BOOLEAN flag.

protectedfunctioncasts(): array
{
return [
'property' => newBooleanCast(),
];
}

Carbon

This accepts any value accepted by the Carbon constructor. If an invalid value is found it will throw a \FriendsOfHyperf\ValidatedDTO\Exception\CastException exception.

protectedfunctioncasts(): array
{
return [
'property' => newCarbonCast(),
];
}

You can also pass a timezone when defining the cast if you need that will be used when casting the value.

protectedfunctioncasts(): array
{
return [
'property' => newCarbonCast('Europe/Lisbon'),
];
}

You can also pass a format when defining the cast to be used to cast the value. If the property has a different format than the specified it will throw a \FriendsOfHyperf\ValidatedDTO\Exception\CastException exception.

protectedfunctioncasts(): array
{
return [
'property' => newCarbonCast('Europe/Lisbon', 'Y-m-d'),
];
}

CarbonImmutable

This accepts any value accepted by the CarbonImmutable constructor. If an invalid value is found it will throw a \FriendsOfHyperf\ValidatedDTO\Exception\CastException exception.

protectedfunctioncasts(): array
{
return [
'property' => newCarbonImmutableCast(),
];
}

You can also pass a timezone when defining the cast if you need that will be used when casting the value.

protectedfunctioncasts(): array
{
return [
'property' => newCarbonImmutableCast('Europe/Lisbon'),
];
}

You can also pass a format when defining the cast to be used to cast the value. If the property has a different format than the specified it will throw a \FriendsOfHyperf\ValidatedDTO\Exception\CastException exception.

protectedfunctioncasts(): array
{
return [
'property' => newCarbonImmutableCast('Europe/Lisbon', 'Y-m-d'),
];
}

Collection

For JSON strings, it will convert into an array first, before wrapping it into a Collection object.

protectedfunctioncasts(): array
{
return [
'property' => newCollectionCast(),
];
}

If you want to cast all the elements inside the Collection, you can pass a Castable to the CollectionCast constructor. Let's say that you want to convert all the items inside the Collection into integers:

protectedfunctioncasts(): array
{
return [
'property' => newCollectionCast(newIntegerCast()),
];
}

This works with all Castable, including DTOCast and ModelCast for nested data.

DTO

This works with arrays and JSON strings. This will validate the data and also cast the data for the given DTO.

This will throw a Hyperf\Validation\ValidationException exception if the data is not valid for the DTO.

This will throw a FriendsOfHyperf\ValidatedDTO\Exception\CastException exception if the property is not a valid array or valid JSON string.

This will throw a FriendsOfHyperf\ValidatedDTO\Exception\CastTargetException exception if the class passed to the DTOCast constructor is not a ValidatedDTO instance.

protectedfunctioncasts(): array
{
return [
'property' => newDTOCast(UserDTO::class),
];
}

Float

If a not numeric value is found, it will throw a FriendsOfHyperf\ValidatedDTO\Exception\CastException exception.

protectedfunctioncasts(): array
{
return [
'property' => newFloatCast(),
];
}

Integer

If a not numeric value is found, it will throw a FriendsOfHyperf\ValidatedDTO\Exception\CastException exception.

protectedfunctioncasts(): array
{
return [
'property' => newIntegerCast(),
];
}

Model

This works with arrays and JSON strings.

This will throw a FriendsOfHyperf\ValidatedDTO\Exception\CastException exception if the property is not a valid array or valid JSON string.

This will throw a FriendsOfHyperf\ValidatedDTO\Exception\CastTargetException exception if the class passed to the ModelCast constructor is not a Model instance.

protectedfunctioncasts(): array
{
return [
'property' => newModelCast(User::class),
];
}

Object

This works with arrays and JSON strings.

This will throw a FriendsOfHyperf\ValidatedDTO\Exception\CastException exception if the property is not a valid array or valid JSON string.

protectedfunctioncasts(): array
{
return [
'property' => newObjectCast(),
];
}

String

If the data can't be converted into a string, this will throw a FriendsOfHyperf\ValidatedDTO\Exception\CastException exception.

protectedfunctioncasts(): array
{
return [
'property' => newStringCast(),
];
}

Create Your Own Type Cast

You can easily create new Castable types for your project by implementing the FriendsOfHyperf\ValidatedDTO\Casting\Castable interface. This interface has a single method that must be implemented:

/** * Casts the given value. * * @param string $property * @param mixed $value * @return mixed */publicfunction 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:

class URLCast implements Castable
{
/** * @param string $property * @param mixed $value * @return URLWrapper */publicfunctioncast(string$property, mixed$value): URLWrapper
{
returnnewURLWrapper($value);
}
}

Then you could apply this to your DTO:

useFriendsOfHyperf\ValidatedDTO\ValidatedDTO;
class CustomDTO extends ValidatedDTO
{
protectedfunctionrules(): array
{
return [
'url' => ['required', 'url'],
];
}
protectedfunctiondefaults(): array
{
return [];
}
protectedfunctioncasts(): array
{
return [
'url' => newURLCast(),
];
}
}

Contact

License

MIT

About

[READ-ONLY]The validated DTO component for Hyperf.

Resources

Security policy

Stars

15 stars

Watchers

2 watching

Forks

Releases

Sponsor this project

Packages

Used by

Contributors

Languages