Skip to content

Design Decisions ​

Non-Goals ​

These features are intentionally out of scope:

  • ORM/Database integration - Keep library focused on data transfer
  • Request handling - Framework-specific, use adapters
  • Dependency injection - Use framework DI containers
  • Runtime reflection - Defeats purpose of code generation

Validation ​

The library provides focused built-in validation:

  • Required Fields
  • Type Checking
  • Common field constraints (minLength, maxLength, min, max, pattern)

For more complex validation, you can use a service like Respect/Validation.

We don't want to bloat the library with a full validation DSL when dedicated validators already solve richer use cases better.

Best Practices ​

  1. Keep DTOs simple - DTOs are data containers, not business logic holders. Consider using a separate validation service for complex rules.

  2. Fail fast - Use required for fields that must always be present. This catches errors at construction time.

  3. Use type hints - PHP's type system already validates types. A string field won't accept an integer.

  4. Validate at boundaries - Validate input at API/form boundaries, not deep in business logic.

  5. Don't over-validate - Internal DTOs used between trusted services don't need the same validation as user input DTOs.

  6. Test validation - Write unit tests for your validation rules.

Custom Casters / Transformers ​

Current features are sufficient:

php
// Input transformation
->factory('fromArray')           // Calls ClassName::fromArray($value)
->factory('fromString')          // Calls ClassName::fromString($value)
->factory('External::create')    // Calls External::create($value)

// Output transformation  
->serialize('array')             // Calls $obj->toArray()
->serialize('string')            // Calls $obj->__toString()
->serialize('FromArrayToArray')  // Full round-trip

Plus auto-detection for:

  • FromArrayToArrayInterface
  • JsonSerializable
  • Enums (backed/unit)

Additional casters add complexity without real benefit.

Readonly Properties (PHP 8.1+) ​

Readonly properties are supported, but they are a deliberate tradeoff rather than the default recommendation.

Tradeoffs:

  1. They change the generated API shape by exposing public readonly properties instead of the usual protected-property plus accessor pattern.
  2. They are stricter than the default mutable model, so they fit best when the DTO is fully initialized up front.
  3. Immutable DTOs with with*() methods remain the more flexible default for most applications.

Verdict: supported for teams that want language-level readonly semantics, but immutable DTOs are still the more ergonomic default in this library.

Best Practices ​

  1. Use immutable DTOs for readonly behavior.
  2. Avoid mixing mutable and immutable DTOs.

Additional Serialization Formats (XML, CSV) ​

Not at this point.

Reason: Out of scope. Use symfony/serializer for complex serialization needs.

Advanced Type Constraints (positive-int, non-empty-string) ​

Not at this point.

Reason: Validation concern, not type concern. Use validation rules instead.

Computed Properties ​

No need on the generated part.

Reason: Use traits instead.

php
// config
Dto::create('User')->traits(\App\Traits\UserComputedTrait::class)->fields(...)

// trait
trait UserComputedTrait {
    public function getFullName(): string {
        return $this->firstName . ' ' . $this->lastName;
    }
}

Lifecycle Hooks ​

Not needed.

Reason: Use traits instead.

php
class UserDto extends AbstractUserDto {
    protected function setFromArray(...): static {
        // pre-processing
        parent::setFromArray(...);
        // post-processing
        return $this;
    }
}

Constructor promotion (PHP 8.0+) ​

The real value of constructor promotion:

  • Reduces boilerplate in hand-written code
  • Our code is generated - verbosity isn't a maintenance burden

Verdict: Not useful for this library. The flexibility of optional fields and incremental population is more valuable than the syntactic sugar of constructor promotion.

Released under the MIT License.