Optional, Lazy & Computed
These attributes control whether a property is present in the input, deferred until serialization, derived instead of hydrated, or hidden from output.
Import them from BradieTilley\Data\Attributes.
Sometimes
Marks a property as optional. Type it as a union with Sometimes. When the value
is absent from the input, it is omitted from toArray() rather than defaulted.
use BradieTilley\Data\Attributes\Sometimes;
class ProfileData extends Data
{
public function __construct(
public string $name,
public Sometimes|array $preferences,
) {}
}
ProfileData::from(['name' => 'Ada'])->toArray();
// ['name' => 'Ada'] — "preferences" is omitted, not null
You can also build values conditionally with Sometimes::when():
Sometimes::when($user->isAdmin(), $sensitiveValue);
// returns $sensitiveValue when true, otherwise a "missing" marker that is
// filtered out of the serialized output
Laravel conditional hydration (WhenLoaded, …) also uses Sometimes|T so a
failed condition becomes an omitted key — see
Laravel Integration.
Lazy
Defers computing a value until serialization. Useful for expensive values that may
never be read. A lazy property is omitted from toArray() unless it is
explicitly requested via
include().
use BradieTilley\Data\Attributes\Lazy;
$data = ReportData::from([
'title' => 'Q1',
'generatedAt' => Lazy::as(fn () => now()),
]);
$data->toArray();
// ['title' => 'Q1'] — "generatedAt" omitted, closure never runs
$data->include('generatedAt')->toArray();
// ['title' => 'Q1', 'generatedAt' => '...'] — closure runs now
Computed
Marks a public property as computed — derived (usually in the constructor body)
rather than hydrated from input. Computed properties are included in toArray()
but never populated from the payload; any input key of the same name is ignored.
They are emitted after the regular properties.
use BradieTilley\Data\Attributes\Computed;
class UserData extends Data
{
#[Computed]
public string $fullName;
public function __construct(
public string $firstName,
public string $lastName,
) {
$this->fullName = "{$firstName} {$lastName}";
}
}
UserData::from(['firstName' => 'Ada', 'lastName' => 'Lovelace'])->toArray();
// ['firstName' => 'Ada', 'lastName' => 'Lovelace', 'fullName' => 'Ada Lovelace']
ExcludeFromOutput
Omits a property from toArray() (and therefore from JSON output). The property is
still hydrated and readable on the instance — it is simply skipped when serializing.
use BradieTilley\Data\Attributes\ExcludeFromOutput;
class UserData extends Data
{
public function __construct(
public string $name,
#[ExcludeFromOutput]
public string $passwordHash,
) {}
}
UserData::from(['name' => 'Ada', 'passwordHash' => '...'])->toArray();
// ['name' => 'Ada'] — "passwordHash" is omitted
For per-call shaping (only, except, include), see
Serialization.