php что такое dto

Чистим пхпшный код с помощью DTO

Это моя первая статья, так что ловить камни приготовился.

Возможно, такой подход в PHP сложился исторически, из-за отсутствия строгой типизации и такого себе ООП. Ведь как по мне, то только с 7 версии можно было более-менее реализовать типизацию+ООП, используя strict_types и type hinting.

Также вызов подобных методов может сопровождаться описанием массива, который мы будем передавать. Или вовсе передается какой-то массив с мусором, а метод просто берет нужные ему ключи. Например, сервис по созданию пользователя:

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

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

Собственно, так и появился мой пакет.

Использование ClassTransformer

С его помощью я автоматически привожу данные к нужному мне классу. Рассмотрим более подробно все также на примере создание пользователя. К нам приходит запрос, и данные из него мы должны отправить в метод. В моем случае на Laravel проекте это выглядит вот так:

В запросе к нам приходит массив параметров: name, phone и email. Пакет просто смотрит есть ли такие параметры у класса, и, если есть, сохраняет значение. В противном случае просто отсеивает их. На входе transform можно передавать не только массив, это может быть другой object, из которого также будут разобраны нужные параметры.

Но наименования аргументов могут отличаться. Тогда, в созданной нами DTO, мы можем спокойно описать свою реализацию приведения:

Существуют объекты гораздо сложнее, с параметрами определенного класса, либо массивом объектов. Что же с ними? Все просто, указываем параметру в PHPDoc путь к классу и все. В случае массива нужно указать, каких именно объектов этот массив:

Указывая параметру класс, вы можете строить более сложные цепочки связей. Пакет рекурсивно будет проходить по всем типизированным параметрам и приведет их к нужному формату.

Путь к классу обязательно должен быть полным, т.к. иначе гораздо сложнее реализовать нахождение класса для инициализации. Или это вообще будет alias и нужно будет парсить файл, а это уже будет оверхед для такого сервиса.

Метод сервиса работает с конкретным набором данным

Знаем все параметры, которые есть у объекта

Можно задать типизацию каждому параметру

Вызов метода становится проще, за счет удаления приведения вручную

В IDE работают все подсказки.

Аналоги

Он пытает решить ту же проблему используя DTO, но их подход заставляет ваш класс наследоваться от их DTO, и самим вызывать инициализацию, передавая в конструктор набор данных. При такой реализации я не вижу особо профита использования, ведь я так же могу сам прописать new DTO() и заполнить параметры.

Roadmap

Реализовать метод afterTransform, который будет вызываться после инициализации DTO. Это позволит более гибко кастомизировать приведение к классу. В данный момент, если входные ключи отличаются от внутренних DTO, нужно самому описывать метод transform. И если у нас из 20 параметров только у одного отличается ключ, нам придется описать приведение всех 20. А с методом afterTransform мы сможем кастомизировать приведение только нужного нам параметра, а все остальные обработает пакет.

Источник

DTO vs POCO vs Value Object

Определения DTO, POCO и Value Object

Вначале небольшая ремарка по поводу Value Object. В C# существует похожая концепция, называемая Value Type. Это всего лишь деталь имплементации того, как объекты хранятся в памяти и мы не будем касаться этого. Value Object, о котором пойдет речь, — понятие из среды DDD (Domain-Driven Design).

Ок, давайте начнем. Вы возможно заметили, что такие понятия как DTO, Value Object и POCO часто используются как синонимы. Но действительно ли они означают одно и то же?

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

С другой стороны, Value Object — это полноценный член вашей доменной модели. Он подчиняется тем же правилам, что и сущности (Entities). Единственное отличие между Value Object и Entity в том, что у Value Object-а нет собственной идентичности. Это означает, что два Value Object-а с одинаковыми свойствами могут считаться идентичными, в то время как две сущности отличаются друг от друга даже в случае если их свойства полностью совпадают.

Value Object-ы могут содержать логику и обычно они не используются для передачи информации между приложениями.

POJO был представлен Мартином Фаулером в качестве альтернативы для JavaBeans и других «тяжелых» enterprise-конструкций, которые были популярны в ранних 2000-х.

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

Другой хороший пример анти-POCO подхода — Entity Framework до версии 4.0. Каждый класс, сгенерированный EF, наследовал от EntityObject, что привносило в домен логику, специфичную для EF. Начиная с версии 4, Entity Framework добавил возможность работать с POCO моделью — возможность использовать классы, которые не наследуются от EntityObject.

Таким образом, понятие POCO означает использование настолько простых классов насколько возможно для моделирования предметной области. Это понятие помогает придерживаться принципов YAGNI, KISS и остальных best practices. POCO классы могут содержать логику.

Корреляция между понятиями

Есть ли связи между этими тремя понятиями? В первую очередь, DTO и Value Object отражают разные концепции и не могут использоваться взаимозаменяемо. С другой стороны, POCO — это надмножество для DTO и Value Object:

php что такое dto. Смотреть фото php что такое dto. Смотреть картинку php что такое dto. Картинка про php что такое dto. Фото php что такое dto

Другими словами, Value Object и DTO не наследуют никаким сторонним компонентам и таким образом являются POCO. В то же время, POCO — это более широкое понятие: это может быть Value Object, Entity, DTO или любой другой класс в том случае если он не наследует компонентам, не относящимся напрямую к решаемой вами проблеме.

Вот свойства каждого из них:

php что такое dto. Смотреть фото php что такое dto. Смотреть картинку php что такое dto. Картинка про php что такое dto. Фото php что такое dto

Заметьте, что POCO-класс может и иметь, и не иметь собственной идентичности, т.к. он может быть как Value Object, так и Entity. Также, POCO может содержать, а может и не содержать логику внутри себя. Это зависит от того, является ли POCO DTO.

Заключение

Вышесказанное в статье можно суммировать следующим образом:

Источник

Php что такое dto

В своей работе я стараюсь придерживаться такого принципа, чтобы код был минимально зависим от фреймворка. Чтобы его можно было при необходимости быстро перенести на другой движок. Чтобы это реализовать, мы должны быть минимально завязаны на таких понятиях, как Model или Request.

В своей работе я стараюсь придерживаться такого принципа, чтобы код был минимально зависим от фреймворка. Чтобы его можно было при необходимости быстро перенести на другой движок. Чтобы это реализовать, мы должны быть минимально завязаны на таких понятиях, как Model или Request.

Об этом же говорят нам принципы проектирования DDD и его реализация на Laravel — Porto.

Аналогичная ситуация возникает и в обратном порядке. Когда мы получили модель и нам требуется обработать в ней данные, как лучше это сделать, абстрагируясь от Laravel?

Здесь есть два варианта. Самый простой из них — преобразовать Request или модель в массив и работать с ним. Но здесь мы сталкиваемся с проблемой подсветки синтаксиса и слабой типизацией. Мы не знаем, какие данные содержатся в массиве и какой тип значений хранится в массивах.

Конечно в такой политике есть свои плюсы и минусы. И главный минус, на мой взгляд, строго типизированные языки обладают меньшими багами. Если переменная имеет неизменяемый тип, то не может произойти и целый ряд последствий. Гарантия типов даёт дополнительную страховку программисту, оберегая его от неожиданных последствий.

Итак, часто вы наверное видели такой код:

Возможно, вы знакомы с проблемой, что мы не знаем, какие данные доступны в массиве validated. Массивы представляют мощную структуру данных только тогда, когда мы используем их для представления списка чего-либо. Но когда мы работаем с большим набором данных, которые нужно преобразовать или обработать, для этого есть другие инструменты.

Это выглядит немного необычно, но предназначение у него такое же как и у массива. Т.е. теперь мы можем переписать предыдущий код следующим образом:

Вы, наверное, заметили, что мы используем метод fromRequest для создания DTO. К счастью, у laravel есть отличная библиотека, которая помогает программистам работать c DTO: https://github.com/spatie/data-transfer-object

По ссылке вы можете ознакомиться с её документацией.

Для подключения этой библиотеки в свой проект, запустите команду:

composer require spatie/data-transfer-object

И теперь мы можем создавать свой собственный DTO. Но для начала давайте создадим прослойку, от которой мы будем наследоваться, чтобы следовать принципам Porto.

Создадим в папке Parents новый файл:

class ObjectData extends \Spatie\DataTransferObject\DataTransferObject

DTO также позволяет работать с коллекциями, нам потребуется и другой родительский класс:

class ObjectDataCollection extends \Spatie\DataTransferObject\DataTransferObjectCollection

Теперь у нас есть всё необходимое, чтобы создать свой первый DTO. Давайте сделаем это на примере класса User:

final class UserData extends \Parents\DataTransferObjects\ObjectData

Источник

Структуры данных в PHP. Data Transfer Object. Предметно-ориентированный Laravel

В основе каждого проекта лежит работа с данными. Почти каждая задача приложения может быть обобщена следующим образом: предоставлять, интерпретировать и манипулировать данными любым способом, который хочет бизнес.

Вы, наверное, и сами это заметили: в начале проекта вы начинаете не с создания контроллеров, а с создания моделей, как их называет Laravel. Крупные проекты выигрывают от создания ERDs и других видов диаграмм для концептуализации того, какие данные будут обрабатываться приложением. Только когда это ясно, вы можете начать создавать точки входа, которые работают с вашими данными.

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

Возможно, вы сейчас думаете о моделях, но это не совсем так. Сначала нам нужно сделать еще несколько шагов назад.

Теория типов

Чтобы понять как использовать объекты передачи данных (DTO) вам нужно будет иметь некоторые базовые знания о типизации.

Давайте уточним несколько терминов, которыми я объясню, как я буду использовать типизацию в этой статье.

Сила типизации – сильная или слабая типизация – определяет, может ли переменная изменить свой тип после того, как она была определена.

PHP – это слабо типизированный язык:

PHP имеет слабую типизацию. Будучи языком, который в основном работает с HTTP-запросом, практически все является строкой.

Даже при наличии включенной строгой типизации система типов PHP слаба. Подсказки типа обеспечивают только тип переменной в этот момент времени, без гарантии относительно любого будущего значения, которое может иметь эта переменная.

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

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

В качестве примечания: это не означает, что строго типизированный язык не может иметь ошибок! Вы прекрасно можете написать глючную реализацию. Но когда строго типизированная программа успешно компилируется, вы уверены, что определенный набор ошибок не может возникнуть в этой программе.

Статические и динамические типы

Есть еще одна концепция, которую мы должны рассмотреть: статические и динамические типы. И именно здесь все начинает становиться интересным.

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

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

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

Эта проверка типов во время выполнения делает PHP динамически типизированным языком. Потому как статически типизированный язык программирования перед выполнением кода выполнит все проверки всех типов в программе при компиляции.

Эти библиотеки могут предложить довольно много информации о вашем коде, без необходимости запускать или модульно тестировать его, IDE, такая как PhpStorm, также имеет множество встроенных статических проверок.

Имея в виду всю эту справочную информацию, пришло время вернуться к ядру нашего приложения: данным.

Структурирование неструктурированных данных

Приходилось ли вам когда-нибудь работать с “массивом данных”, который на самом деле был больше, чем просто список? Вы использовали ключи массива в качестве полей? И вы чувствовали боль от того, что не знали точно, что было в этом массиве? Не будучи уверенным, действительно ли данные в нем такие, какими вы их ожидаете увидеть, или какие поля доступны?

Давайте представим себе, о чем я говорю: работа с запросами Laravel. Рассмотрим этот пример как базовую операцию CRUD для обновления существующего клиента:

Прежде чем искать решения этой проблемы, вот что вы можете сделать, чтобы понять что находится в этой свалке:

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

Оказывается, что строго типизированные системы в сочетании со статическим анализом могут быть большим подспорьем в понимании того, с чем именно мы имеем дело. Такие языки, как Rust, например, решают эту проблему просто:

Структура – это то, что нам нужно! К сожалению, PHP не имеет структур. Но у него есть массивы и объекты. Объектов может быть достаточно для решения нашей проблемы:

Типизированные свойства доступны только с PHP 7.4. В зависимости от того, когда вы читаете эту статью, вы можете еще не использовать их — у меня есть решение для вас позже в этой статье, продолжайте читать.

Для тех, кто может использовать PHP 7.4 или выше, вы можете сделать что-то вроде этого:

Статический анализатор, встроенный в вашу IDE, всегда сможет сообщить нам, с какими данными мы имеем дело.

Этот шаблон упаковки неструктурированных данных в типы, чтобы мы могли использовать наши данные надежным способом, называется “объекты передачи данных” или DTO (Data Transfer Object). Этот шаблон я настоятельно рекомендую вам использовать в ваших проектах большего размера. Для средних и маленьких проектов данный шаблон будет избыточен.

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

По моему опыту, однако, есть больше преимуществ в строго типизированном подходе при работе с командой из нескольких разработчиков над проектом в течение серьезного количества времени. Вы должны использовать любую возможность, чтобы уменьшить когнитивную нагрузку. Вы не хотите, чтобы разработчикам приходилось начинать отладку своего кода каждый раз, когда они хотят знать, что именно находится в переменной. Информация должна быть прямо под рукой, чтобы разработчики могли сосредоточиться на том, что важно: создании приложения.

Конечно, использование DTO имеет свою цену: существуют не только накладные расходы на определение этих классов; Вам также нужно сопоставить, например, запрос данных с DTO.

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

Однако вопрос о построении DTO из “внешних” данных все еще нуждается в ответе.

Фабрики DTO

Как мы строим DTO? Я поделюсь с вами двумя возможностями, а также объясню, какая из них имеет мое личное предпочтение.

Первый-самый правильный: использование специальной фабрики.

Наличие отдельной фабрики сохраняет ваш код чистым на протяжении всего проекта. Эта фабрика должна жить в прикладном слое.

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

Очевидно, что смешивание кода конкретного приложения в рамках домена-не самая лучшая идея. Однако у него есть мое предпочтение. На то есть две причины.

Во-вторых, и это более важная причина; я предпочитаю этот подход, потому что одно из собственных ограничений PHP: он не поддерживает именованные параметры.

Видите ли, вы не хотите, чтобы ваши DTO в конечном итоге имели конструктор с индивидуальным параметром для каждого свойства: это не масштабируется и очень сбивает с толку при работе со свойствами nullable или default-value. Вот почему я предпочитаю подход передачи массива в DTO, и пусть он строит себя на основе данных в этом массиве.

Поскольку именованные параметры не поддерживаются, статический анализ также недоступен, а это означает, что вы находитесь в неведении о том, какие данные необходимы при построении DTO. Я предпочитаю держать это “пребывание в темноте” в классе DTO, чтобы его можно было использовать без лишних мыслей извне.

Если бы PHP поддерживал что-то вроде именованных параметров, я бы использовал такой шаблон:

До тех пор, пока PHP не поддержит это, я бы предпочел прагматическое решение теоретически правильному. Но это зависит от тебя. Не стесняйтесь выбирать то, что подходит вашей команде лучше всего.

Альтернатива типизированным свойствам

Как я уже упоминал ранее, существует альтернатива использованию типизированных свойств для поддержки DTO: docblocks. Пакет DTO от Spatie, который выше применяли, также поддерживает их.

Однако по умолчанию docblocks не дают никаких гарантий, что данные относятся к тому типу, о котором они говорят. К счастью, PHP имеет свой API рефлексии, и с ним возможно гораздо больше.

Решение, предоставляемое этим пакетом, можно рассматривать как расширение системы типизации PHP. Если вы не можете использовать PHP 7.4 и хотите немного больше уверенности в том, что ваши типы docblock действительно соблюдаются, этот пакет вам поможет.

Поскольку данные лежат в основе почти каждого проекта, это один из самых важных строительных блоков. Объекты передачи данных предлагают вам способ работы с данными структурированным, безопасным и предсказуемым способом.

Источник

Php что такое dto

Using Data Transfer Objects (DTOs)

As stated in the general design considerations, in most cases the DTO pattern should be implemented using an API Resource class representing the public data model exposed through the API and a custom data provider. In such cases, the class marked with #[ApiResource] will act as a DTO.

However, it’s sometimes useful to use a specific class to represent the input or output data structure related to an operation.

Specifying an Input or an Output Data Representation

For a given resource class, you may want to have a different representation of this class as input (write) or output (read). To do so, a resource can take an input and/or an output class:

The input and output attributes are taken into account by all the documentation generators (GraphQL and OpenAPI, Hydra).

php что такое dto. Смотреть фото php что такое dto. Смотреть картинку php что такое dto. Картинка про php что такое dto. Фото php что такое dto

To simplify object transformations we have to implement a Data Transformer that will convert the input into a resource or a resource into an output.

We have the following BookInput :

When using serialization groups, you need to specify these to the class and also to the Input itself. We can transform the BookInput to a Book resource instance:

We now register it:

To manage the output, it’s exactly the same process. For example, we have the following BookOutput :

We can transform the Book to a BookOutput object:

We now register it:

Updating a Resource with a Custom Input

When performing an update (e.g. PUT operation), the resource to be updated is read by API Platform before the deserialization phase. To do so, it uses a data provider with the :id parameter given in the URL. The body of the request is the JSON object sent by the client, it is deserialized and is used to update the previously found resource.

php что такое dto. Смотреть фото php что такое dto. Смотреть картинку php что такое dto. Картинка про php что такое dto. Фото php что такое dto

Now, we will update our resource by using a different input representation.

With the following BookInput :

We will implement a BookInputDataTransformer that transforms the BookInput to our Book resource instance. In this case, the Book ( /books/1 ) already exists, so we will just update it.

Initialize the Input DTO For Partial Update

In order to be able to do a partial update ( PATCH ), it is needed to initialize the input DTO with the existing data before the deserialization process.

This way, the input DTO will be correctly validated with its old data and partial new data.

Create a class implementing the DataTransformerInitializerInterface instead of the DataTransformerInterface :

Disabling the Input or the Output

Per Operation input and output

input and output attributes can be set on a per operation basis:

When specified, input and output attributes support:

Using Objects As Relations Inside Resources

The Book attribute property will now be an instance of Attribute after the (de)normalization phase.

Validating Data Transfer Objects

Before transforming DTO to your API Resource you may want to ensure that the DTO has valid data. In this case we can inject the validator to your data transformer and validate it.

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *