Хочете прочитати цей контент португальською? [Натисніть тут для доступу до версії на португальській мові]🇧🇷�
TLDR: Уявіть, що у вас є система на PHP, де кілька частин обмінюються інформацією — форми, API, інтеграції з іншими сервісами. У кожній точці входу потрібно переконатися, що вхідні дані мають правильний формат, без відсутніх полів або небезпечних значень. Часто ми розкидаємо isset()
, filter_var()
і ручні перевірки по коду, що ускладнює підтримку та створює потенційні недоліки.
Зіткнувшись з цими ситуаціями, я вирішив створити свій власний модуль DTO, натхненний найкращими практиками з інших мов (як Java та C#) та існуючими бібліотеками в екосистемі PHP (наприклад, Spatie, Symfony Validator тощо). Ідея полягає в тому, щоб централізувати транспортування, очищення та перевірку даних в одному місці, уникнути повторення логіки та підвищити безпеку.
Що таке DTO?
DTO (Data Transfer Object) — це простий об'єкт, основною метою якого є транспортування даних між частинами системи. Іншими словами, замість того щоб безпосередньо відкривати доменні сутності або працювати з неструктурованими асоціативними масивами, ми створюємо клас (DTO), який точно відображає форму/структуру даних, що передаються з одного місця в інше.
- Без складної бізнес-логіки: Зазвичай, DTO не повинно містити важких бізнес-правил.
- Орієнтоване на дані: Зазвичай містить тільки властивості (атрибути) та, в крайньому випадку, методи доступу (геттери/сеттери).
- Уникає зв'язування: Використовуючи DTO, ви змінюєте спосіб представлення даних для кожного випадку, не впливаючи на інші шари системи.
Приклад:
В цьому середовищі має сенс безпосередньо застосовувати концепцію DTO у PHP, використовуючи ці нові можливості.
Основна ідея полягає в тому, щоб використовувати DTO як центральну точку для транспортування даних, їх валідації та очищення в одному місці, декларативно та організовано. Таким чином, ми уникаємо розкидання перевірок даних та «очищення» по різних частинах коду (контролери, сервіси тощо), що зменшує ймовірність неузгодженостей або забутих правил у деяких місцях.
Основна структура та використання атрибутів
Для ілюстрації уявімо базовий клас (наприклад, 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
Надають контрольований доступ до властивостей, перевіряючи їх на наявність, перевіряючи типи, викликаючи санітайзер та виконуючи валідацію за необхідності.
Ключове тут — це атрибути PHP 8+, які використовуються для визначення, які валідатори та санітайзери застосовуються до кожної властивості. Наприклад:
8])]
#[Sanitize(TrimSanitizer::class)]
public ?string $password = null;
}
У цьому прикладі:
email
є обов'язковим (RequiredValidator
), має бути у правильному форматі email (EmailValidator
), та має бути очищений від пробілів і приведений до нижнього регістру (LowercaseSanitizer
,TrimSanitizer
).password
є обов'язковим, повинно містити щонайменше 8 символів, і перед валідацією також очищується.
Як це працює на практиці
З точки зору використання, ми можемо зробити щось на зразок цього:
'[email protected] ',
'password' => ' secret123'
];
try {
// Створюємо DTO з масиву
$userDto = UserDto::fromArray($userData, true, true);
// Тепер $userDto->email буде "[email protected]"
// А $userDto->password буде "secret123"
// Якщо потрібно, можемо конвертувати назад в масив
$arrayOutput = $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 все більш організованою, безпечною та практичною, особливо коли йдеться про дані, які потребують регулярної валідації та очищення.
До зустрічі наступного разу! 🚀
Перекладено з: How a DTO Module Can Simplify (and Strengthen) Your PHP Data Flow