Skip to content

DTO Foundation #91

Description

@derpixler

API Reference: Intro

All infrastructure that the concrete DTOs depend on — without this, no DTO compiles.

DTOInterface defines the contract every DTO must fulfill: fromArray(array): static, toArray(): array, id(): ?int, plus JsonSerializable so DTOs work with json_encode(). It carries a @template annotation for static analysis.

Cast provides type-safe coercion from API response data. Instead of every DTO calling (bool) $data['active'] (which turns "false" into true), Cast::boolOrNull() handles string-to-bool conversion correctly. Same for dateTime() (ISO 8601 → DateTimeImmutable), string() (with default), and intOrNull(). One file, tested once, used everywhere.

HasTimestamps trait supplies $created_at / $updated_at properties (both ?DateTimeImmutable, default null) and two static helpers: createdAt(array $data) and updatedAt(array $data) that call Cast::dateTime() internally. Eight of nine DTOs use this — TagDTO is the only exception since Zammad tags don't carry timestamps. Eliminates 16 identical lines across the codebase.

DtoHydrator offers a reflection-based alternative to manual fromArray(). It caches property metadata per DTO class and hydrates via constructor parameter reflection — no more hand-mapping 10+ fields per DTO. HydratesFromArray and SerializesToArray traits provide default implementations a DTO can use if it wants to skip writing fromArray() and toArray() manually.

Acceptance Criteria

  • Testing: DtoHydratorTest — reflection hydration, meta-cache, missing fields return null, DateTimeImmutable parsing
  • Documentation in README: README "Quick Start" — $ticket->title property access with IDE autocomplete

Depends on

Produces

  • DTOInterface
  • Cast
  • HasTimestamps
  • DtoHydrator
  • HydratesFromArray
  • SerializesToArray

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    Fields

    No fields configured for Task.

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions