kamilkozak.dev
Published on

DTO w Laravel – Strukturyzacja danych i typy w PHP

Dlaczego strukturyzacja danych ma znaczenie?

W świecie Laravela dane są wszędzie: formularze, API, bazy danych, pliki konfiguracyjne. W małych projektach możesz pozwolić sobie na swobodę – np. używać tablic z kluczami jak $data['user_name']. Ale gdy aplikacja rośnie, chaos narasta:

  • Nowy developer nie wie, jakie pola istnieją w tablicy $validated z żądania.
  • Zmiana typu pola (np. z string na int) powoduje błędy w nieoczekiwanych miejscach.
  • Testy stają się koszmarem przez brak gwarancji struktury danych.

Strukturyzacja danych to fundament dla przewidywalności. Dzięki niej:

  • Każdy fragment kodu wie, czego może się spodziewać.
  • IDE podpowiada dostępne pola i typy.
  • Zmniejszasz ryzyko błędów „na produkcji”.

System typów w PHP

PHP jest elastyczny i pozwala na wiele, ale czasem płacisz za to cenę.

  • Słabe typowanie:
$value = "100"; // string
$value = $value + 5; // teraz to int (105)

Brak kontroli nad typami prowadzi do niespodzianek, np. gdy "abc" zamienia się w 0 w obliczeniach.

  • Dynamiczne typowanie: Błędy typów (np. przekazanie string zamiast int) wykrywane są dopiero w runtime’ie. To oznacza, że aplikacja może crashować w nieprzewidzianych momentach.

Ratunek? Wykorzystaj statyczne analizatory jak PHPStan lub Psalm, które działają jak „czujniki typów” przed uruchomieniem kodu. Potrafią wykryć problemy z typami, podobnie jak proces kompilacji w językach silnie typowanych.

Wyzwanie nieustrukturyzowanych danych

Wyobraź sobie endpoint API, który przyjmuje dane użytkownika:

public function updateUser(Request $request) {
    $data = $request->all();
    User::find($data['id'])->update($data);
}

Problemy:

  • Co jeśli $data['id'] nie istnieje?
  • Czy $data['birthday'] jest stringiem, czy obiektem Carbon?
  • Gdy dodasz nowe pole phone_number, musisz przeszukać cały kod, aby znaleźć miejsca, gdzie jest używane.

Rozwiązanie? Zastąp tablice obiektami danych, które wymuszają strukturę i typy.

Obiekty danych (DTOs) – ratunek dla czytelności kodu

DTO (Data Transfer Object) to klasa, która:

  • Definiuje konkretne pola (np. name, email).
  • Wymusza określone typy (np. string, Carbon).
  • Może zawierać logikę walidacji (np. sprawdzanie formatu emaila).

Przykład:

class UserProfileData {
    public function __construct(
        public string $name,
        public string $email,
        public Carbon $birthday,
        public ?string $phone = null
    ) {}
}

Dlaczego to działa?

  • Każdy, kto używa UserProfileData, wie, że birthday jest obiektem Carbon.
  • IDE podpowie wszystkie dostępne pola.
  • Błędy literówek (np. emial zamiast email) znikają.

Tworzenie obiektów danych w praktyce

Mapowanie żądania HTTP na DTO:

public function update(UserProfileRequest $request) {
    $data = new UserProfileData(
        name: $request->input('name'),
        email: $request->input('email'),
        birthday: Carbon::parse($request->input('birthday')),
        phone: $request->input('phone')
    );
    // ... użyj $data
}

Wyzwanie: Ręczne parsowanie pól (np. Carbon::parse()) jest uciążliwe. Rozwiązanie: Użyj metody fabrykującej w DTO:

class UserProfileData {
    public static function fromRequest(UserProfileRequest $request): self {
        return new self(
            $request->input('name'),
            $request->input('email'),
            Carbon::parse($request->input('birthday')),
            $request->input('phone')
        );
    }
}

Laravel Data – ułatwiaj sobie życie

Paczka spatie/laravel-data automatyzuje tworzenie DTOs w Laravelu.

Kluczowe funkcje:

  • Automatyczna konwersja typów (np. string → Carbon).
  • Integracja z walidacją Laravela:
use Spatie\LaravelData\Attributes\Validation\Email;
use Spatie\LaravelData\Data;

class UserData extends Data {
    public function __construct(
        public string $name,
        #[Email]
        public string $email
    ) {}
}

// W kontrolerze:
public function store(UserData $data) {
    // $data jest już zwalidowane!
}
  • Generowanie typowania TypeScript – idealne dla frontendu.

Bonus: DTOs mogą być używane jako zasoby API (API Resources), co utrzymuje spójność między backendem a frontendem.

Podsumowanie: Korzyści ze strukturyzacji danych

  1. Mniej błędów – typy są sprawdzane na etapie tworzenia DTO.
  2. Lepsza dokumentacja – struktura danych jest jawna w kodzie.
  3. Łatwiejsze testy – mockowanie danych staje się przewidywalne.
  4. Bezpieczeństwo – unikasz SQL injection przez przypadkowe przekazanie nieoczekiwanych pól.

Jak zacząć?

  • W małych projektach: Zacznij od ręcznego tworzenia DTO dla kluczowych żądań.
  • W dużych aplikacjach: Zintegruj Laravel Data, aby zautomatyzować proces.

Pamiętaj: DTOs to nie tylko „kolejna warstwa abstrakcji”. To inwestycja w długoterminowe zdrowie kodu – szczególnie, gdy pracujesz w zespole lub projekt ma żyć latami. 🛠️