- PHP 8.2 or higher
- Laravel 11
for dev
- PHPUnit &11
- phpstan ^1.11
- php-cs-fixer ^3.58
Install package via composer
composer require aneterial/laravel-data-validatorYou now have a class attribute DataValidator\Attributes\RequestProperty at your disposal. Add it to the properties of your DTO and set the necessary configuration fields
use DataValidator\Attributes\RequestProperty;
final readonly class ExampleDTO {
#[RequestProperty(property: 'id', rules: 'required|integer|min:0')]
public int $id;
#[RequestProperty(property: 'email', rules: 'required|string|email')]
public string $email;
}Description of fields:
property: name of the request key that matches the propertyrules: validation rules based on component semantics Laravel Validation, accepts only string valuerequestDataType: to indicate where a field is expected - in the request body (default)const RequestProperty::BODY_TYPEor in query stringconst RequestProperty::QUERY_TYPElistRules: if the value is an array (list) - set the validation rules for each element according to the semantics of Laravel Validation
Next, you need to get a DataValidator\DataManager instance in your controller from app DI container and pass the request essence to it, indicating the DTO class that you expect to receive after validation and filling with data.
$dataManager = app(\DataValidator\DataManager::class);Next, the Laravel validator will check the request entity (instanse of \Illuminate\Http\Request), and if the data is incorrect, it will throw an \Illuminate\Validation\ValidationException. If the data is correct, the Manager will create and fill the DTO object with data, which you can use in your application
/** @var ExampleDTO $dto */
$dto = $dataManager->validateAndConvert(from: $request, to: ExampleDTO::class);If your endpoint involves passing array of objects [{...}, {...}, {...}], you can use a method that will validate the request and return an array of DTOs
/** @var ExampleDTO[] $dtos */
$dtos = $dataManager->validateAndConvertList(from: $request, to: ExampleDTO::class);Here are some examples of using validation by attributes
- DTO with lists
final readonly class ExampleDTO {
...
/** @var string[] $emails */
#[RequestProperty(property: 'emails', rules: 'required|list', listRules: 'string|email')]
public array $emails;
/** @var int[] $ids */
#[RequestProperty(property: 'ids', rules: 'required|list', listRules: 'int|min:0')]
public array $ids;
...
}- DTO with non required properties, if it not required - it should be nullable, except array - it can be empty array
final readonly class ExampleDTO {
#[RequestProperty(property: 'id', rules: 'integer|min:0')]
public ?int $id;
#[RequestProperty(property: 'email', rules: 'string|email')]
public ?string $email;
/** @var int[] $ids */
#[RequestProperty(property: 'ids', rules: 'list', listRules: 'int|min:0')]
public array $ids;
}- DTO with nested object
final readonly class ExampleDTO {
...
#[RequestProperty(property: 'child', rules: 'required|array')]
public NestedDTO $child;
...
}
// NestedDTO should contain properties with attributes
final readonly class NestedDTO {
#[RequestProperty(property: 'id', rules: 'required|integer|min:0')]
public int $id;
#[RequestProperty(property: 'email', rules: 'required|string|email')]
public string $email;
}- DTO with list of nested object
final readonly class ExampleDTO {
...
/** @var NestedDTO[] $children */
#[RequestProperty(property: 'children', rules: 'required|list', listRules: NestedDTO::class)]
public array $children;
...
}- DTO with enum of
BackedEnumproperty, you should set enum type to property and no more rules are required except, if you want - indicate type of enum
final readonly class ExampleDTO {
...
#[RequestProperty(property: 'enum', rules: 'required|string')]
public AnApplicationEnum $enum;
...
}- DTO with enums of
BackedEnumproperty
final readonly class ExampleDTO {
...
/** @var AnApplicationEnum[] $enums */
#[RequestProperty(property: 'enums', rules: 'required|list', listRules: AnApplicationEnum::class)]
public array $enums;
...
}- Important note: for different requestDataTypeif DTO has nested objects or arrays of nested objects, the requestDataType of these entities is ignored and taken from the parrent entity
So if you use
final readonly class ExampleDTO {
...
#[RequestProperty(property: 'child', rules: 'required|array', requestDataType: RequestProperty::BODY_TYPE)]
public NestedDTO $child;
...
}
// NestedDTO should contain properties with attributes
final readonly class NestedDTO {
#[RequestProperty(property: 'id', rules: 'required|integer|min:0', requestDataType: RequestProperty::QUERY_TYPE)]
public int $id;
#[RequestProperty(property: 'email', rules: 'required|string|email', requestDataType: RequestProperty::QUERY_TYPE)]
public string $email;
}In nested object requestDataType will not work and it be RequestProperty::BODY_TYPE like in parrent entity
- Another one: for list validationMethod DataManager::validateAndConvertList can only work with data from request body, so all properties of entity will be force casted to type RequestProperty::BODY_TYPE in this usage