Skip to content

Nested Dtos

Learn how to work with complex nested Dto structures.

Nested Dtos allow you to compose complex data structures from simpler Dtos:

class AddressDto extends SimpleDto
{
public function __construct(
public readonly string $street,
public readonly string $city,
public readonly string $country,
) {}
}
class UserDto extends SimpleDto
{
public function __construct(
public readonly string $name,
public readonly AddressDto $address,
) {}
}
$user = UserDto::fromArray([
'name' => 'John Doe',
'address' => [
'street' => '123 Main St',
'city' => 'New York',
'country' => 'USA',
],
]);
class ProfileDto extends SimpleDto
{
public function __construct(
public readonly string $bio,
public readonly string $avatar,
) {}
}
class UserDto extends SimpleDto
{
public function __construct(
public readonly string $name,
public readonly ProfileDto $profile,
) {}
}
class UserDto extends SimpleDto
{
public function __construct(
public readonly string $name,
public readonly ?AddressDto $address = null,
) {}
}
class CityDto extends SimpleDto
{
public function __construct(
public readonly string $name,
public readonly string $zipCode,
) {}
}
class AddressDto extends SimpleDto
{
public function __construct(
public readonly string $street,
public readonly CityDto $city,
) {}
}
class UserDto extends SimpleDto
{
public function __construct(
public readonly string $name,
public readonly AddressDto $address,
) {}
}
class OrderItemDto extends SimpleDto
{
public function __construct(
public readonly string $product,
public readonly int $quantity,
public readonly float $price,
) {}
}
class OrderDto extends SimpleDto
{
public function __construct(
public readonly int $orderId,
public readonly array $items, // Array of OrderItemDto
) {}
}
$order = OrderDto::fromArray([
'orderId' => 123,
'items' => [
['product' => 'Widget', 'quantity' => 2, 'price' => 10.00],
['product' => 'Gadget', 'quantity' => 1, 'price' => 20.00],
],
]);
use event4u\DataHelpers\SimpleDto\DataCollection;
class OrderDto extends SimpleDto
{
public function __construct(
public readonly int $orderId,
public readonly DataCollection $items,
) {}
}
class ProductDto extends SimpleDto
{
public function __construct(
public readonly int $id,
public readonly string $name,
public readonly float $price,
) {}
}
class OrderItemDto extends SimpleDto
{
public function __construct(
public readonly ProductDto $product,
public readonly int $quantity,
) {}
#[Computed]
public function total(): float
{
return $this->product->price * $this->quantity;
}
}
class ShippingAddressDto extends SimpleDto
{
public function __construct(
public readonly string $street,
public readonly string $city,
public readonly string $zipCode,
public readonly string $country,
) {}
}
class OrderDto extends SimpleDto
{
public function __construct(
public readonly int $orderId,
public readonly DataCollection $items,
public readonly ShippingAddressDto $shippingAddress,
public readonly Carbon $orderDate,
) {}
#[Computed]
public function total(): float
{
return $this->items->sum(fn($item) => $item->total());
}
}
// ✅ Good - with type hint
public readonly AddressDto $address;
// ❌ Bad - no type hint
public readonly $address;
// ✅ Good - 2-3 levels
UserDto -> AddressDto -> CityDto
// ❌ Bad - too deep
UserDto -> ProfileDto -> SettingsDto -> PreferencesDto -> ThemeDto
// ✅ Good - use DataCollection
public readonly DataCollection $items;
// ❌ Bad - plain array
public readonly array $items;