laravel-validated-dto
repository·main·Indexed 20 days ago
https://github.com/wendelladriel/laravel-validated-dtoA Laravel package for creating type-safe Data Transfer Objects (DTOs) with built-in validation, default values, and property casting. It supports instantiating DTOs from arrays, JSON strings, Laravel Request objects, Eloquent models, and Artisan commands. Starting with v3, DTOs can be type-hinted directly in controller actions to act like Form Request classes for automatic resolution via the service container.
What's inside laravel-validated-dto
- Validated DTO is a package for Laravel that provides Data Transfer Objects (DTOs) with built-in validation. It allows you to define typed objects that validate incoming data using standard Laravel validation rules. This ensures that data moving between different layers of your application (such as from an HTTP request to a service class, or from a queued job) adheres to a single, consistent, and validated data contract.
Key features of Validated DTO
mainThe package offers several capabilities for managing data integrity in Laravel:
- Laravel Integration: Seamlessly integrates into existing Laravel applications.
- Standard Validation: Uses existing Laravel validation rules.
- Typed Properties: Uses public PHP properties for type safety.
- Advanced Casting: Supports casting for scalar values, objects, collections, models, enums, and nested DTOs.
- Customization: Allows defining custom validation messages and attribute names.
- Mapping: Supports mapping between incoming and outgoing property names.
- API & Frontend: Enables building API response objects with resource DTOs and generating TypeScript definitions from DTO classes.
- Livewire Support: Compatible with Laravel Livewire.
Implement custom data transformation
mainTo implement custom transformation logic (e.g., mapping keys or changing formats), override or use the
buildDataForExport()method. This method returns the validated data with your custom mappings applied, which can then be used by other transformation methods or converted to other types.public function toObject(): object { return (object) $this->buildDataForExport(); }Use the EmptyCasts trait to simplify DTOs
mainIf you are using the#[Cast]attribute on properties, or if your DTO requires no casting at all, you can use theWendellAdriel\ValidatedDTO\Concerns\EmptyCaststrait. This prevents the need to manually implement acasts()method that returns an empty array.How nested data transformation works
mainWhen transforming a DTO, the transformation logic is applied recursively to all properties. The behavior depends on the property type:
Property Type Transformation Behavior Scalar Data TypeandArrayPreserves original values stdClassTransformed into arrays via type casting UnitEnumTransformed into the case name BackedEnumTransformed into the case value CarbonandCarbonImmutableTransformed into strings via ->toISOString()Collection,Model, andDTORecursively transformed using the same rules above Understand property filtering in DTOs
mainDTOs automatically filter incoming data based on the
rulesmethod. If you pass keys in the constructor array that are not explicitly listed in your DTO'srulesmethod, those properties will be ignored and will not be accessible on the resulting object.// If 'username' is NOT defined in UserDTO::rules()... $dto = new UserDTO([ 'name' => 'John Doe', 'email' => 'john.doe@example.com', 'password' => 's3CreT!@1a2B', 'username' => 'john_doe', ]); $dto->username; // This will not be availableUnderstand the default DTO methods
mainGenerated DTOs are designed to be simple and include only the most essential methods. The following methods are included by default:
rules(): Used specifically for Validated DTOs to define validation logic.defaults(): Defines default values for properties.casts(): Defines how properties should be cast.
If your implementation requires more advanced functionality, you can manually add the following methods to your DTO class:
messages()attributes()mapData()mapToTransform()
Choose the right DTO type
mainThe package provides three primary DTO types. Choose based on your specific use case:
ValidatedDTO: Use when incoming data must be validated before the DTO is considered ready. This is the recommended default.SimpleDTO: Use when you need typed data, casts, mapping, and transforms, but do not require validation.ResourceDTO: Use when the DTO is primarily an API response object intended to be returned directly from controllers.
Getting started with Validated DTO
mainIf you are adding Validated DTO to an application for the first time, follow these documentation steps in order:
- Installation
- Configuration
- Generating DTOs
- Defining DTO properties
- Defining validation rules
- Creating DTO instances
- Accessing DTO data
- Type casting
Map DTO properties to different formats during transformation
mainIf you need the output of your DTO (when calling
toArray(),toJson(), ortoModel()) to have different keys than the DTO properties themselves, use these methods:mapToTransform()method: Override this protected method to return an array mapping DTO property paths to the desired output keys. This is useful for flattening nested DTO data into a flat Model or Array.#[Map(transform: '...')]attribute: Apply this to a property to specify its name in the transformed output.
You can combine
mapData()(for input) andmapToTransform()(for output) to create a complete pipeline:Input Key->DTO Property->Output Key.// Using mapToTransform() to flatten nested DTO data for a Model class UserDTO extends ValidatedDTO { public NameDTO $name; protected function mapToTransform(): array { return [ 'name.first_name' => 'first_name', 'name.last_name' => 'last_name', ]; } } // Using #[Map] attribute for transformation use WendellAdriel//ValidatedDTO//Attributes//Map; class UserDTO extends ValidatedDTO { #[Map(transform: 'username')] public string $name; }Create custom type casts using Callables
mainFor simpler casting logic that doesn't require a dedicated class, you can use a
callable(such as an anonymous function/closure) directly within thecasts()method of yourValidatedDTO.The callable must accept two arguments:
string $propertyandmixed $value, and should return the casted value.class CustomDTO extends ValidatedDTO { protected function rules(): array { return ['url' => ['required', 'url']]; } protected function casts(): array { return [ 'url' => function (string $property, mixed $value) { return new URLWrapper($value); }, ]; } }Enable Lazy Validation in a ValidatedDTO
mainBy default,
ValidatedDTOperforms validation and attribute casting during instantiation. If you want to instantiate a DTO without immediate validation (e.g., to allow manual property assignment before validation), you can enable Lazy Validation.When lazy validation is enabled:
- You can instantiate the DTO without providing initial attributes.
- Setting properties on the DTO will not trigger automatic casting.
- Data is only validated and attributes are only cast when you explicitly call the
validate()method.
If
validate()passes, the attributes are cast within the DTO object. If it fails, anIlluminate\Validation\ValidationExceptionis thrown.class LazyDTO extends ValidatedDTO { public bool $lazyValidation = true; } $dto = new LazyDTO(); $dto->name = 'John Doe'; $dto->validate(); // Validation and casting happen here