Introduction
Simple Data Objects is a lightweight PHP 8.4+ library for creating typed Data Transfer Objects (DTOs) using constructor property promotion (or plain typed properties — see below) and PHP attributes.
Why Simple Data Objects?
DTOs are ubiquitous in modern PHP applications — they carry data between layers, validate input from HTTP requests, and serialize to/from APIs. Yet most implementations involve a lot of repetitive plumbing code.
Simple Data Objects removes that plumbing:
use StdOut\SimpleDataObjects\BaseData;
use StdOut\SimpleDataObjects\Attributes\Rules;
use StdOut\SimpleDataObjects\Attributes\Cast;
use StdOut\SimpleDataObjects\Casts\DateTimeCast;
class CreateOrderData extends BaseData
{
public function __construct(
#[Rules(['required', 'string', 'max:200'])]
public readonly string $title,
#[Rules(['required', 'email'])]
public readonly string $customerEmail,
#[Cast(new DateTimeCast('Y-m-d'))]
public readonly DateTime $deliveryDate,
public readonly ?string $notes = null,
) {}
}
// Validate + hydrate in one step
$order = CreateOrderData::fromValidated($request->all());
$order->title; // 'New laptop'
$order->deliveryDate; // DateTime object
$order->toArray(); // ['title' => ..., 'customerEmail' => ..., 'deliveryDate' => '2025-01-15', ...]
$order->toJson(); // JSON stringCore Concepts
Hydration
from() is a universal factory: it accepts arrays, Eloquent models and any Arrayable, stdClass, JsonSerializable, any Traversable, JSON strings, and plain objects. Nested DTOs, enums, and collections are resolved automatically. tryFrom() returns null instead of throwing; fromResult() goes further and never throws either, collecting every field's error (dot-pathed for nested DTOs and collections) instead of stopping at the first one.
Serialization
toArray() and toJson() serialize the object back to its wire format. Casts apply in both directions — hydration reads them, serialization writes them.
Attributes
All behaviour is declared on the constructor parameters:
#[Cast(...)]— type conversion for dates, booleans, JSON, encryption, …#[Rules([...])]— Laravel validation rules#[Hidden]— exclude fromtoArray()output#[IgnoreIfNull]— omit null fields from output#[Flatten]— inline nested DTO fields into the parent array#[MapPropertyName]/#[MapInputName]/#[MapOutputName]/#[TransformKeys]— key remapping
Performance
Reflection runs once per class per process, results are cached in memory. Enable file-based cache with MetadataRegistry::setStoragePath() to have opcache pre-compile all metadata across requests.
Requirements
| Dependency | Version |
|---|---|
| PHP | ^8.4 |
illuminate/contracts | ^10 | ^11 | ^12 | ^13 |
illuminate/support | ^10 | ^11 | ^12 | ^13 |
illuminate/validation | ^10 | ^11 | ^12 | ^13 |
Works standalone — no Laravel application required. Validation uses the library's own minimal validator bootstrap when no container is available.