Віддаєте перевагу читанню цієї статті англійською? [Клікніть тут, щоб переглянути англійську версію]
Уявіть, що у вас є PHP система, де різні частини обмінюються інформацією — форми, API, інтеграції з іншими сервісами. В кожній точці входу вам потрібно переконатися, що отримані дані мають правильний формат, без відсутніх полів або небезпечних значень. Часто ми розкидаємо виклики isset()
, filter_var()
і ручні перевірки по всьому коду, що ускладнює обслуговування та відкриває можливість для помилок.
Саме зіштовхнувшись з такими ситуаціями, я вирішив створити власний модуль DTO, натхненний кращими практиками з інших мов програмування (наприклад, Java і C#) та існуючими бібліотеками в PHP екосистемі (як-от Spatie, Symfony Validator тощо). Ідея полягає в тому, щоб централізувати транспортування, санітизацію та валідацію даних в одному місці, уникаючи повторення логіки та підвищуючи безпеку.
Що таке DTO?
DTO (Data Transfer Object) — це простий об'єкт, основна мета якого — транспортувати дані між частинами системи. Тобто, замість того, щоб безпосередньо викривати доменні сутності або працювати з неструктурованими асоціативними масивами, ми створюємо клас (DTO), який точно представляє форму/структуру даних, що передаються з одного місця в інше.
- Без складної бізнес-логіки: DTO традиційно не має реалізовувати важку бізнес-логіку.
- Фокус на даних: Зазвичай містить лише властивості (атрибути) і, максимум, методи доступу (гетери/сетери).
- Уникає зчеплення: Використовуючи DTO, ми змінюємо спосіб представлення даних для кожного конкретного випадку, не впливаючи на інші шари системи.
Приклад:
Isso gera código duplicado e dificulta garantir que todos os pontos estejam validados e sanitizados de forma consistente.
Então, surgiu a ideia: por que não unir (1) o transporte de dados, (2) a validação e (3) a sanitização em um único lugar, de forma declarativa?
DTOs e PHP
Розробляю на PHP вже понад 20 років і протягом цього часу слідкував за еволюцією мови — від процедурного стилю до появи MVC-фреймворків і, зрештою, до PHP 8+, який приніс attributes (анотації) як потужний і універсальний інструмент. У цьому контексті має сенс застосувати концепцію DTO безпосередньо в PHP, використовуючи нові можливості мови.
Основна ідея — використовувати DTO як центральну точку для транспортування даних, валідації та санітизації в одному місці, декларативно та організовано. Таким чином, ми уникаємо того, щоб валідації та “очищення” даних розкидувались по різних частинах коду (контролери (Controller), сервіси (Service) тощо), зменшуючи ймовірність появи неузгодженостей або забуття правил у деяких місцях.
Основна структура та використання атрибутів
Для прикладу, уявімо базовий клас (наприклад, BaseDto
), який надає методи, такі як:
fromArray(array $data, bool $sanitize = true, bool $validate = true): static
Створює екземпляр DTO з асоціативного масиву. Потім виконується перетворення типів, застосовуються санітизатори і валідатори (якщо вони увімкнені).toArray(): array
Конвертує DTO назад в масив, рекурсивно проходячи через підоб'єкти, які також можуть бути DTO.get(string $property): mixed
таset(string $property, mixed $value): void
Забезпечують контрольований доступ до властивостей, перевіряючи їх на наявність, валідуючи типи, викликаючи санітизацію та тригеруючи валідацію, коли це необхідно.
Ключовим моментом є використання attributes PHP 8+, щоб визначити для кожної властивості, які валидатори і санітизатори мають бути застосовані. Наприклад:
8])]
#[Sanitize(TrimSanitizer::class)]
public ?string $password = null;
}
У цьому прикладі ми визначаємо, що:
email
є обов'язковим (RequiredValidator
), має бути у правильному форматі електронної пошти (EmailValidator
) і має пройти санітизацію для видалення пробілів та приведення до нижнього регістру (LowercaseSanitizer
,TrimSanitizer
).password
є обов'язковим, має містити щонайменше 8 символів і, перед валідацією, також проходить "очищення".
Як це працює на практиці
У термінах використання ми могли б мати ось таке:
'[email protected] ',
'password' => ' secret123'
];
try {
// Створюємо DTO з масиву
$userDto = UserDto::fromArray($userData, true, true);
// Тепер $userDto->email має значення "[email protected]"
// А $userDto->password має значення "secret123"
// Можемо перетворити назад в масив, якщо потрібно
$arraySaida = $userDto->toArray();
// ["email" => "[email protected]", "password" => "secret123"]
} catch (\Exception $ex) {
// Якщо валідація не пройшла, викидається виняток з деталями
// Ви можете обробити або залогувати ці помилки
}
Зверніть увагу, що при виклику fromArray()
весь процес перетворення типів, санітизації та валідації відбувається в єдиному процесі.
Якщо яке-небудь поле не відповідає вимогам валідаторів (наприклад, якщо email
порожнє або неправильне), викидається виняток, який описує проблему, що полегшує обробку помилок.
Елегантність і Потужність
Цей підхід “DTO + Валідатори + Санітизатори через атрибути” має кілька очевидних переваг:
- Централізація Логіки: Усі правила очищення та валідації знаходяться поруч із властивостями, які їх потребують, що полегшує розуміння та підтримку.
- Простота Оголошення: Переглянувши клас
UserDto
, ви відразу розумієте, які поля існують, як їх потрібно валідувати та які трансформації вони зазнають перед збереженням або використанням в інших частинах системи. - Гнучкість: Нічого не заважає вам використовувати готові валідатори/санітизатори або створювати власні, що відповідають специфічним правилам вашої доменної області (наприклад, CPF, CNPJ, спеціальні формати дат тощо).
- Ізоляція: DTO може передаватися між різними шарами (контролер → сервіс → репозиторій тощо) без залежності від того, як кожен шар обробляє збереження або бізнес-логіку. Якщо зміниться модель бази даних, це не “ламатиме” сам процес транспортування даних.
Підсумовуючи, поєднання DTO (фокусування на транспортуванні даних) з атрибутами для валідації та санітизації дає PHP потужність, подібну до тієї, що ми бачимо в інших мовах, але без необхідності великих фреймворків чи складних конфігурацій. Це “декларативний” стиль програмування, що робить код більш виразним, безпечним та простим для підтримки.
Висновок
Використання DTO допомагає організувати транспортування даних між шарами та сервісами, але можна піти далі: включення валідації та санітизації в цю модель робить потік даних ще більш надійним і безпечним. У PHP цей підхід набирає популярності завдяки атрибутам, які значно спрощують налаштування кожної властивості.
Якщо вам сподобалася ідея і ви хочете побачити функціональний приклад того, як структурувати таке рішення, перегляньте мій репозиторій на GitHub, де я публікую багаторазово використовувані компоненти PHP:
Головний репозиторій:
https://github.com/phrbarbosa/sandbox-php
Шлях до модуля DTO:
https://github.com/phrbarbosa/sandbox-php/tree/main/src/Dto
Не соромтеся досліджувати, адаптувати та робити внесок. Ідея полягає в тому, щоб зробити розробку на PHP ще більш організованою, безпечною і практичною, особливо коли мова йде про дані, які потрібно постійно валідувати та санітизувати.
До зустрічі наступного разу! 🚀
Перекладено з: Como um módulo DTO pode simplificar (e fortalecer) seu fluxo de dados em PHP