Collections
TypedDataCollection
TypedDataCollection<T> extends Laravel's Collection with generic type hints so IDEs know the element type.
From a DTO class
$collection = UserData::collection([
['name' => 'Alice', 'email' => 'alice@example.com'],
['name' => 'Bob', 'email' => 'bob@example.com'],
]);
$collection->first(); // UserData — IDE infers type
$collection->last(); // UserData
$collection->count(); // 2Static factory
use StdOut\SimpleDataObjects\TypedDataCollection;
$collection = TypedDataCollection::of(UserData::class, $rawItems);Pass-through existing instances
Already-hydrated instances are passed through without re-hydration:
$user = UserData::from([...]);
$collection = UserData::collection([$user, [...]]);
// $collection->first() === $userLazy Collections
lazyCollection() returns an Illuminate\Support\LazyCollection that hydrates items one at a time as they are consumed, instead of materializing everything upfront. Use it for large iterables — a DB cursor, a generator streaming a big CSV — where holding every hydrated instance in memory at once would be wasteful:
$names = UserData::lazyCollection($csvRowGenerator)
->take(3)
->map(fn (UserData $u) => $u->name)
->all();
// only 3 rows were ever hydrated, no matter how large the source isLike collection(), already-hydrated instances pass through unchanged.
Nested Collections in DTOs
Use #[DataCollection(ClassName::class)] to declare a property as a typed collection:
use StdOut\SimpleDataObjects\Attributes\DataCollection;
use StdOut\SimpleDataObjects\TypedDataCollection;
class TeamData extends BaseData
{
public function __construct(
public readonly string $name,
#[DataCollection(UserData::class)]
public readonly TypedDataCollection $members,
) {}
}
$team = TeamData::from([
'name' => 'Engineering',
'members' => [
['name' => 'Alice', 'email' => 'alice@example.com'],
['name' => 'Bob', 'email' => 'bob@example.com'],
],
]);
$team->members->count(); // 2
$team->members->first()->name; // 'Alice' — typedCollection Methods
All standard Laravel Collection methods are available:
$collection
->filter(fn (UserData $u) => str_contains($u->email, '@company.com'))
->map(fn (UserData $u) => $u->with(phone: null))
->each(fn (UserData $u) => $mailer->send($u->email))
->toArray();Serialization
A collection in a DTO serializes each element via toArray():
$team->toArray();
// [
// 'name' => 'Engineering',
// 'members' => [
// ['name' => 'Alice', 'email' => 'alice@example.com'],
// ['name' => 'Bob', 'email' => 'bob@example.com'],
// ],
// ]