В современных веб-приложениях обычно используются разные уровни приложения, такие как уровень базы данных, уровень бизнес-логики и уровень представления. Каждый уровень отвечает за определенную задачу и взаимодействует с другими уровнями через интерфейсы. Однако при передаче данных между этими слоями могут возникнуть трудности с управлением данными и обеспечением их правильной передачи. Вот тут-то и появляются объекты передачи данных (DTO).

DTO — это шаблон проектирования, используемый для передачи данных между уровнями приложения. DTO — это объект, который переносит данные между процессами, например, между веб-приложением и базой данных. Это простой контейнер для данных, в котором нет никакой бизнес-логики, проверки или других операций.

Одним из наиболее распространенных применений DTO является передача данных между уровнями приложения. Например, при извлечении данных из базы данных данные могут быть возвращены в виде массива или набора результатов базы данных. Однако уровень бизнес-логики может потребовать, чтобы данные были в определенном формате или типе. DTO можно использовать для преобразования данных из одного формата в другой и обеспечения их правильной передачи на следующий уровень.

Другое использование DTO — определение структуры объектов ответа, возвращаемых API. При разработке API важно определить структуру объектов ответа, чтобы обеспечить их согласованность и простоту использования клиентскими приложениями. DTO можно использовать для определения структуры объекта ответа и обеспечения того, чтобы он содержал все необходимые данные.

Теперь, когда вы хорошо понимаете, что такое объекты передачи данных (DTO) и почему они полезны в PHP-приложениях, пришло время погрузиться в некоторые практические примеры. Хотя теорию, лежащую в основе DTO, важно понимать, она может показаться немного суховатой без реального контекста.

  1. UserDTO создан статическим методом
class UserDTO {
    private int $id;
    private string $name;
    private string $email;

    private function __construct(int $id, string $name, string $email) {
        $this->id = $id;
        $this->name = $name;
        $this->email = $email;
    }

    public static function create(int $id, string $name, string $email): UserDTO {
        return new self($id, $name, $email);
    }

    public function id(): int {
        return $this->id;
    }

    public function name(): string {
        return $this->name;
    }

    public function email(): string {
        return $this->email;
    }
}

В этом примере класс UserDTO имеет три частных свойства: $id, $name и $email. Конструктор сделан закрытым, чтобы гарантировать, что экземпляр класса может быть создан только с использованием статического метода create.

Метод create принимает три свойства в качестве аргументов и возвращает новый объект UserDTO с этими свойствами. Методы id, name и email обеспечивают доступ только для чтения к закрытым свойствам класса.

Использование частного конструктора и статического метода для создания класса помогает гарантировать, что данные в DTO действительны и могут использоваться по назначению, предотвращая при этом любые внешние манипуляции с данными.

Использование частных свойств и методов получения более традиционно и может использоваться в более ранних версиях PHP. Это позволяет вам определять свойства отдельно от конструктора, что может сделать код более модульным и простым для тестирования. Это также обеспечивает лучшую инкапсуляцию, поскольку свойства являются закрытыми и доступны только через методы получения. Однако для реализации этого подхода требуется больше кода, и он может быть менее кратким и удобочитаемым, чем подход public readonly.

2. UserDTO, использующий общедоступный доступ только для чтения

class UserDTO {
    private function __construct(
        public readonly int $id,
        public readonly string $name,
        public readonly string $email
    ) {}

    public static function create(int $id, string $name, string $email): UserDTO {
        return new self($id, $name, $email);
    }
}

Этот подход с использованием свойств public readonly доступен только в PHP 8.1 и более поздних версиях. Это позволяет вам определять свойства непосредственно в сигнатуре конструктора, что может сделать код более кратким и удобным для чтения. Он также обеспечивает неизменность «из коробки», поскольку свойства являются общедоступными и доступны только для чтения. Однако этот подход не предоставляет каких-либо методов доступа к значениям свойств, что в некоторых ситуациях может быть недостатком.

Таким образом, оба подхода имеют свои преимущества и недостатки, и выбор между ними зависит от конкретных потребностей вашего приложения. Если вы используете PHP 8.1 или более позднюю версию и вам требуется неизменность, подход public readonly может быть хорошим выбором. В противном случае подход с закрытыми свойствами и методами получения обеспечивает лучшую инкапсуляцию и может использоваться в более ранних версиях PHP.

В качестве бонуса, если вам интересно, как это работает с использованием трейтов. Пожалуйста, ознакомьтесь с моей другой статьей.