Projectsphp-dataNested And Collections

PHP Data

Package

Lightweight, framework-agnostic Data Transfer Object (DTO) library with first-class Laravel integration.

Nested & Collection Data

Data objects compose. A property typed as another Data class is recursively hydrated and serialized.

Nested data

class AddressData extends Data
{
    public function __construct(
        public string $street,
        public string $city,
    ) {}
}

class UserData extends Data
{
    public function __construct(
        public string $name,
        public AddressData $address,
    ) {}
}

$user = UserData::from([
    'name' => 'Ada',
    'address' => ['street' => '1 Main St', 'city' => 'London'],
]);

$user->address; // AddressData instance
$user->toArray();
// ['name' => 'Ada', 'address' => ['street' => '1 Main St', 'city' => 'London']]

The nested address array is automatically turned into an AddressData.

Arrays of data

Use #[ArrayOf] (or a @var array<…, Type> phpdoc) to hydrate every element of an array:

use BradieTilley\Data\Attributes\ArrayOf;

class TeamData extends Data
{
    public function __construct(
        public string $name,
        #[ArrayOf(UserData::class)]
        public array $members,
    ) {}
}

TeamData::from([
    'name' => 'Core',
    'members' => [
        ['name' => 'Ada', 'address' => ['street' => '…', 'city' => '…']],
        ['name' => 'Alan', 'address' => ['street' => '…', 'city' => '…']],
    ],
]);
// members is a list of hydrated UserData

Non-Data element types (e.g. DateTime, backed enums, Carbon) are cast the same way a scalar property of that type would be:

#[ArrayOf(DateTime::class)]
public array $releaseDates;
#[ArrayOf('string')]
public Collection $tags;

The same mechanism casts arrays of custom value objects via IterableItemCast:

class ReleaseData extends Data
{
    public function __construct(
        public string $title,
        /**
         * @var array<int, DateTime>
         */
        public array $releaseDates,
    ) {}
}

Collections of data

When Laravel is installed, a property typed Illuminate\Support\Collection with #[ArrayOf] hydrates into a Collection of items:

use Illuminate\Support\Collection;

class TeamData extends Data
{
    public function __construct(
        #[ArrayOf(UserData::class)]
        public Collection $members,
    ) {}
}

Collecting many at once

Data::collect() maps an iterable of inputs into many Data instances:

$people = UserData::collect([
    ['name' => 'Ada', 'address' => [...]],
    ['name' => 'Alan', 'address' => [...]],
]);
  • With Laravel installed, collect() returns an Illuminate\Support\Collection.
  • Without Laravel, it returns a plain array — see Framework-agnostic usage.