Simple Slim DTO
Problem
Large DTO libraries often solve far more than a small response or integration DTO needs. That is useful in bigger domains, but sometimes I only want a readable schema: public typed properties, predictable input mapping, nested DTO casting, and a clean array output.
I still like and recommend spatie/laravel-data, but in some projects I only use a small part of its feature set. Simple Slim DTO is the smaller tool I reach for when the DTO should stay close to the payload shape and avoid a broad abstraction layer.
Installation
composer require pepperfm/ssd-for-laravel
Requirements
- PHP
^8.4
Basic Usage
Extend BaseDto and describe the payload with public typed properties.
use Pepperfm\Ssd\BaseDto;
class ResponseWrapperDto extends BaseDto
{
public array $data;
public LinksDto $links;
public MetaDto $meta;
}
Create DTOs from arrays, objects, Arrayable, Traversable, or named arguments.
$dto = ResponseWrapperDto::make([
'data' => $response['data'],
'links' => $response['links'],
'meta' => $response['meta'],
]);
$dto = new ResponseWrapperDto(
data: $response['data'],
links: $response['links'],
meta: $response['meta'],
);
Nested DTO properties are cast automatically.
use Pepperfm\Ssd\BaseDto;
class LinksDto extends BaseDto
{
public ?string $first = null;
public ?string $last = null;
public ?string $prev = null;
public ?string $next = null;
}
class MetaDto extends BaseDto
{
public int $currentPage;
public int $total;
}
Iterable Casting
Use ToIterable when a property contains a list of items.
The first argument is the collection type. Use ToIterable::ARRAY for a native array.
The second argument is optional and defines the item type.
use Pepperfm\Ssd\Attributes\ToIterable;
use Pepperfm\Ssd\BaseDto;
class ResponseDataDto extends BaseDto
{
public string $id;
public string $name;
}
class ResponseWrapperDto extends BaseDto
{
#[ToIterable(ToIterable::ARRAY, ResponseDataDto::class)]
public array $data = [];
}
/** @var array<array-key, ResponseDataDto> $items */
$items = $dto->data;
For Laravel collections, pass the collection class as the first argument.
use Illuminate\Support\Collection;
use Pepperfm\Ssd\Attributes\ToIterable;
use Pepperfm\Ssd\BaseDto;
class ResponseWrapperDto extends BaseDto
{
#[ToIterable(Collection::class, ResponseDataDto::class)]
public Collection $data;
}
/** @var Collection<array-key, ResponseDataDto> $items */
$items = $dto->data;
If the item type is omitted, iterable items are kept as-is.
use Pepperfm\Ssd\Attributes\ToIterable;
use Pepperfm\Ssd\BaseDto;
class TagsDto extends BaseDto
{
#[ToIterable(ToIterable::ARRAY)]
public array $tags = [];
}
The item type may be another BaseDto class or any class that can be instantiated from one value.
Current ToIterable signature
Use #[ToIterable(collectionType, itemType)]. The old one-argument DTO form is intentionally not accepted because the first argument now always describes the collection type.
Key Mapping
Input keys can be camelCase or snake_case. Output keys are camelCase by default.
Use MapName when the external key must be explicit.
use Pepperfm\Ssd\Attributes\MapName;
use Pepperfm\Ssd\BaseDto;
class UserDto extends BaseDto
{
#[MapName('external_name')]
public string $displayName;
}
$dto = UserDto::make([
'external_name' => 'Ada',
]);
$dto->displayName; // Ada
$dto->external_name; // Ada
$dto->toArray(); // ['external_name' => 'Ada']
Collections and Helpers
Convert many payloads into DTO instances with collect().
$items = ResponseDataDto::collect($response['data']);
Use only() and except() on serialized DTO output.
$dto->only('data', 'meta');
$dto->except('links');
Serialization
$dto->toArray();
$dto->jsonSerialize();
json_encode($dto);
Testing
composer test
composer lint