Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Validated Dto Laravel Package

wendelladriel/laravel-validated-dto

Build typed Data Transfer Objects for Laravel that validate incoming data using familiar validation rules, defaults, and casting. Create DTOs by extending ValidatedDTO, define rules(), and get safe, validated, ready-to-use properties for your app.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require wendelladriel/laravel-validated-dto
    
  2. Create your first DTO:

    php artisan make:dto User
    

    This generates a stub with ValidatedDTO base class, rules(), defaults(), and casts() methods.

  3. Define validation rules (e.g., in app/Dtos/UserDTO.php):

    protected function rules(): array
    {
        return [
            'name' => ['required', 'string', 'max:255'],
            'email' => ['required', 'email'],
        ];
    }
    
  4. Instantiate and use:

    $userDTO = UserDTO::fromRequest(); // Automatically validates request data
    $userDTO->name; // Access validated data
    

First Use Case: API Request Handling

Replace manual validation in controllers with DTOs:

// Before (Controller)
public function store(Request $request)
{
    $validated = $request->validate([
        'name' => 'required|string',
        'email' => 'required|email',
    ]);
    // ...
}

// After (Controller)
public function store(UserDTO $dto) // Automatically injects and validates
{
    // $dto->name and $dto->email are guaranteed to be valid
}

Implementation Patterns

1. DTO as Request Wrapper

Workflow:

  • Use fromRequest() to bind to incoming requests (web/API).
  • Leverage Laravel’s dependency injection to auto-resolve DTOs in controllers.

Example:

// Controller
public function update(UserDTO $dto)
{
    $user = User::find($dto->id);
    $user->update($dto->toArray());
}

2. Nested DTOs for Complex Structures

Pattern:

  • Use Receive attribute to nest DTOs (e.g., AddressDTO inside UserDTO).
  • Automatically validates nested data.

Example:

use WendellAdriel\ValidatedDTO\Attributes\Receive;

final class UserDTO extends ValidatedDTO
{
    #[Receive]
    public AddressDTO $address;

    // ...
}

3. Lazy Validation for Performance

Use Case:

  • Defer validation until data is accessed (e.g., for bulk operations).

Implementation:

$dto = UserDTO::fromArray($data, lazy: true);
$dto->name; // Validates on first access

4. DTOs in Services

Pattern:

  • Pass DTOs between services to enforce validation boundaries.
  • Use toArray() or toJson() for output.

Example:

// Service
public function process(UserDTO $dto)
{
    $this->userRepository->create($dto->toArray());
}

5. Custom Casting

Extend Default Casts:

use WendellAdriel\ValidatedDTO\Casting\Cast;

final class UserDTO extends ValidatedDTO
{
    #[Cast('WendellAdriel\ValidatedDTO\Casting\DateTimeCast')]
    public Carbon $createdAt;
}

6. DTOs in Artisan Commands

Pattern:

  • Use fromArray() with command arguments.

Example:

// Command
protected $signature = 'user:create {--name= : User name} {--email= : User email}';

public function handle()
{
    $dto = UserDTO::fromArray([
        'name' => $this->option('name'),
        'email' => $this->option('email'),
    ]);
    // ...
}

7. DTOs with Eloquent Models

Bidirectional Transformation:

// Convert Model to DTO
$userDTO = UserDTO::fromModel($user);

// Convert DTO to Model
$user->fill($dto->toArray());

8. DTOs in Tests

Pattern:

  • Use DTOs to validate API responses or service outputs.

Example:

$response = $this->post('/users', $data);
$response->assertValid();
$dto = UserDTO::fromJson($response->content());
$this->assertEquals('John', $dto->name);

Gotchas and Tips

Pitfalls

  1. Validation Timing:

    • DTOs validate on instantiation by default. Use lazy: true to defer validation.
    • Example: $dto = UserDTO::fromArray($data, lazy: true);
  2. Circular References:

    • Avoid bidirectional DTO nesting (e.g., UserDTO containing AddressDTO which contains UserDTO).
    • Fix: Use #[SkipOnTransform] to exclude properties during serialization.
  3. Default Values Override:

    • defaults() are applied after validation. Use sometimes rules for optional fields.
    • Example:
      protected function rules(): array
      {
          return ['active' => ['sometimes', 'boolean']];
      }
      protected function defaults(): array
      {
          return ['active' => true]; // Only applied if 'active' is not in input
      }
      
  4. Mass Assignment:

    • DTOs do not auto-fill models. Use toArray() explicitly:
      $user->fill($dto->toArray()); // Correct
      $user->fill($dto); // Fails (DTO is not an array)
      
  5. Custom Casts:

    • Ensure custom casts implement Casting\Cast interface.
    • Common Issue: Forgetting to return the cast value in cast() method.
      public function cast($value): ?string
      {
          return $value ? 'yes' : 'no'; // Must return casted value
      }
      
  6. Nested DTO Validation:

    • Nested DTOs validate recursively. Use #[Receive] for top-level nesting.
    • Gotcha: Nested DTOs must have their own rules() defined.
  7. Type Safety:

    • PHP 8.2+ typed properties are enforced. Older versions may require runtime checks.
    • Tip: Use #[Assert\Type] for runtime type validation (e.g., #[Assert\Type('array')]).
  8. Performance:

    • Avoid overusing DTOs for simple requests. For lightweight cases, use Laravel’s built-in validation.

Debugging Tips

  1. Validation Errors:

    • Access errors via $dto->errors() or $dto->failed().
    • Example:
      if ($dto->failed()) {
          return response()->json($dto->errors(), 422);
      }
      
  2. Data Inspection:

    • Use $dto->getData() to inspect raw input before validation.
    • Use $dto->toArray() to see validated output.
  3. Stub Customization:

    • Publish the stub template:
      php artisan vendor:publish --tag="validated-dto-stubs"
      
    • Modify stubs/dto.stub for project-specific defaults.
  4. Lazy Validation:

    • Check if validation was deferred:
      if ($dto->isLazy()) {
          $dto->validate(); // Force validation
      }
      

Extension Points

  1. Custom Transformers:

    • Extend WendellAdriel\ValidatedDTO\Transformers\DataTransformer to modify output.
    • Override transform() method in your DTO:
      protected function transform(array $data): array
      {
          $data['full_name'] = "{$data['first_name']} {$data['last_name']}";
          return $data;
      }
      
  2. Custom Validation Hooks:

    • Use afterValidation() for post-validation logic:
      protected function afterValidation(): void
      {
          $this->name = strtoupper($this->name);
      }
      
  3. Custom Casts:

    • Create reusable casts in app/Casts/:
      namespace App\Casts;
      use WendellAdriel\ValidatedDTO\Casting\Cast;
      
      class CustomCast implements Cast
      {
          public function cast($value): string
          {
              return strtoupper($value);
          }
      }
      
    • Use in DTO:
      #[Cast('App\Casts\CustomCast')]
      public string $name;
      
  4. DTO Events:

    • Listen for dto.validated and dto.failed events:
      event(new ValidatedDTOEvent($dto));
      

Configuration Quirks

  1. Published Config:

    • The package publishes a minimal config (config/validated-dto.php).
    • Key settings:
      • strict_mode: Throw exceptions on validation failure (default: false).
      • casts: Global cast mappings.
  2. Strict Mode:

    • Enable to fail fast on validation errors:
      'strict_mode
      
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
calmfox/watch-sylius
damienfern/grpc-symfony-bundle
atoolo/index-bundle
atoolo/genai-bundle
coprotoai/laravel-ticket
davidjln/llm-carbon-bundle
cryonighter/valid-request-bundle
coolms/taxonomy-bundle
coolms/field-bundle
articulate-orm/symfony
aaix/laravel-tall-architect
ephoto/akeneo-connector
emmanuelballery/eb-plantumlbundle
emielburgman/symfony-visitor-beacon
emielburgman/symfony-visit-storage
emielburgman/symfony-security-headers
emielburgman/symfony-log-viewer
emarref/xdebug-bundle
emarref/pubnub-bundle
elriseio/finance-money-bundle