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

Value Object Laravel Package

spatie/value-object

Deprecated Spatie package for PHP 8+ data transfer objects. Create typed DTOs from arrays with casting, validation, and attribute mapping (e.g., nested keys). Consider migrating to spatie/laravel-data or cuyz/valinor.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Installation:

    composer require spatie/data-transfer-object
    

    (Note: Requires PHP 8.0+ for v3. For older versions, use v2.)

  2. Define a DTO:

    use Spatie\DataTransferObject\DataTransferObject;
    
    class UserDto extends DataTransferObject
    {
        public string $name;
        public int $age;
    }
    
  3. Instantiate and Use:

    $dto = UserDto::from([
        'name' => 'John Doe',
        'age' => 30,
    ]);
    
    echo $dto->name; // "John Doe"
    

First Use Case

  • API Request Validation: Convert incoming request data into a strongly-typed DTO for validation and processing.
    $dto = UserDto::from(request()->all());
    if ($dto->age < 18) {
        abort(400, 'User must be at least 18 years old.');
    }
    

Where to Look First


Implementation Patterns

Core Workflows

  1. DTO Creation:

    • Extend DataTransferObject and define properties.
    • Use from(array) to populate from associative arrays (e.g., API requests, database rows).
    • Use fromJson(string) for JSON payloads.
  2. Validation:

    • Leverage PHP’s type system for basic validation (e.g., string $name enforces strings).
    • Add custom validation via rules() or validate() methods (if extending further).
    • Example:
      if (!$dto->validate()) {
          throw new \InvalidArgumentException($dto->errors()->first());
      }
      
  3. Transformation:

    • Use DTOs to normalize data from multiple sources (e.g., API + database).
    • Example:
      $user = User::find(1);
      $dto = UserDto::from($user->toArray());
      
  4. Immutable Data:

    • DTOs are immutable by default (properties are read-only after creation).
    • Use hydrate(array) to create mutable copies if needed.
  5. Nested DTOs:

    • Support nested DTOs for complex structures.
    • Example:
      class AddressDto extends DataTransferObject {
          public string $street;
          public string $city;
      }
      
      class UserDto extends DataTransferObject {
          public string $name;
          public AddressDto $address;
      }
      

Integration Tips

  • API Layer:

    • Use DTOs to standardize request/response shapes across controllers.
    • Example:
      public function store(Request $request) {
          $dto = UserDto::from($request->validated());
          // Process $dto...
      }
      
  • Service Layer:

    • Pass DTOs between services to enforce contracts.
    • Example:
      $service->process(UserDto::from($request->all()));
      
  • Testing:

    • Mock DTOs for unit tests to isolate logic.
    • Example:
      $dto = new UserDto();
      $dto->name = 'Test User';
      $this->assertEquals('Test User', $dto->name);
      
  • Database Seeding:

    • Use DTOs to seed complex data structures.
    • Example:
      User::create(UserDto::from([
          'name' => 'Admin',
          'email' => 'admin@example.com',
          'roles' => ['admin', 'editor'],
      ])->toArray());
      

Gotchas and Tips

Pitfalls

  1. Immutability:

    • Properties are read-only after creation. Avoid direct assignment:
      $dto = new UserDto();
      $dto->name = 'John'; // Throws Error!
      
    • Fix: Use from() or hydrate() to create new instances.
  2. Type Safety:

    • PHP’s type system is enforced, but runtime type checks (e.g., is_string()) are not.
    • Tip: Use assert() or custom validation for critical fields:
      assert(is_string($dto->name), 'Name must be a string!');
      
  3. Nested DTO Initialization:

    • Nested DTOs must be initialized via from() or constructor args.
    • Gotcha: Passing raw arrays to nested properties fails silently:
      $dto = UserDto::from([
          'address' => ['street' => '123 Main'] // Fails if AddressDto expects strict typing.
      ]);
      
    • Fix: Ensure nested DTOs are properly hydrated.
  4. Magic Methods:

    • DTOs rely on __get() and __set(). Overriding these breaks hydration.
    • Tip: Avoid overriding magic methods unless you know what you’re doing.
  5. Deprecation Warning:

Debugging

  1. Validation Errors:

    • Use $dto->errors() to inspect validation failures:
      $dto = UserDto::from(['name' => 123]);
      $dto->validate(); // Fails
      print_r($dto->errors()); // ['name' => 'Must be a string.']
      
  2. Property Access:

    • Undefined properties throw Error (not UndefinedPropertyException).
    • Tip: Use property_exists($dto, 'name') for safe checks.
  3. JSON Serialization:

    • DTOs implement JsonSerializable, but nested objects may not serialize as expected.
    • Fix: Ensure nested DTOs also implement JsonSerializable.

Tips

  1. Custom Validation:

    • Extend the base class to add validation logic:
      class ValidatedUserDto extends DataTransferObject {
          public string $name;
          public int $age;
      
          public function rules(): array {
              return [
                  'name' => 'required|string|min:2',
                  'age' => 'integer|min:18',
              ];
          }
      }
      
  2. Mass Assignment Protection:

    • Use fillable property to whitelist allowed fields:
      class UserDto extends DataTransferObject {
          public string $name;
          public string $email;
      
          protected array $fillable = ['name', 'email'];
      }
      
  3. Performance:

    • DTOs are lightweight. Use them for:
      • API request/response payloads.
      • Database query results.
      • Avoid overusing for simple scalar data (e.g., prefer arrays for trivial cases).
  4. IDE Support:

    • Enable PHPStan or Psalm for static analysis of DTO properties.
    • Example PHPStan config:
      includes:
          - vendor/spatie/data-transfer-object/src/DataTransferObject.php
      
  5. Migration Path:

    • If adopting spatie/laravel-data, note key differences:
      • Laravel-specific features (e.g., Eloquent model casting).
      • Different syntax for nested objects (e.g., ->address vs. ->addressDto).
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.
davejamesmiller/laravel-breadcrumbs
artisanry/parsedown
christhompsontldr/phpsdk
enqueue/dsn
bunny/bunny
enqueue/test
enqueue/null
enqueue/amqp-tools
milesj/emojibase
bower-asset/punycode
bower-asset/inputmask
bower-asset/jquery
bower-asset/yii2-pjax
laravel/nova
spatie/laravel-mailcoach
spatie/laravel-superseeder
laravel/liferaft
nst/json-test-suite
danielmiessler/sec-lists
jackalope/jackalope-transport