Увійти Реєстрація
Блог Серії
Кар'єра
Вакансії Компанії
Навчання
Документація Співбесіди Тестування Відео
Екосистема
Пакети Ресурси Проєкти Інструменти Події
Інше
Про нас Реклама

Навіщо DTO в Laravel-застосунку і як їх оформлювати?

DTO (Data Transfer Object) - простий об'єкт для передачі даних між шарами застосунку: типізовані поля без бізнес-поведінки.

Проблема масивів:

public function handle(array $data): Order
{
    $data['customer_id'];   // а чи є такий ключ? який тип? як він називається - customerId?
}

Масив не має контракту: невідомо, які ключі обов'язкові, яких типів, - доводиться шукати, звідки він прийшов. Помилки в назвах ключів знаходяться лише під час виконання.

DTO на сучасному PHP:

final readonly class PlaceOrderData
{
    /** @param list<OrderLineData> $lines */
    public function __construct(
        public int $customerId,
        public array $lines,
        public ?string $comment = null,
        public DeliveryMethod $delivery = DeliveryMethod::Courier,
    ) {}

    public static function fromRequest(StoreOrderRequest $request): self
    {
        return new self(
            customerId: $request->user()->id,
            lines: array_map(OrderLineData::fromArray(...), $request->validated('items')),
            comment: $request->validated('comment'),
            delivery: DeliveryMethod::from($request->validated('delivery')),
        );
    }
}

Що дає DTO:

  • явний контракт: з сигнатури handle(PlaceOrderData $data) видно, що потрібно операції;
  • типи й підказки IDE, перевірка статичним аналізом;
  • незмінність (readonly): дані не зміняться непомітно по дорозі;
  • незалежність від джерела: та сама дія викликається з HTTP (fromRequest), команди Artisan, імпорту CSV, тестів;
  • енуми й об'єкти-значення замість рядків.

Де DTO корисні найбільше:

  • вхід бізнес-операцій (action-класи, сервіси);
  • відповіді сторонніх API - перетворити JSON на типізовані об'єкти одразу на межі, а не тягнути масиви по коду;
  • межі модулів - модуль віддає DTO, а не свої моделі Eloquent.

Інструменти:

  • readonly-класи PHP 8.2+ з властивостями конструктора - достатньо для більшості випадків;
  • spatie/laravel-data - DTO з вбудованою валідацією, перетворенням з/у масив, Request, моделі, генерацією TypeScript-типів;
  • Form Request + метод toDto().

Чого уникати: DTO на кожну модель «про всяк випадок», які дублюють поля моделі один в один, - це шар без користі. DTO вводять там, де потрібен контракт між частинами коду.

Докладніше в документації: PHP: readonly-класи

Схожі питання