php что такое dto

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

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

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

php что такое dto

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

Заключение

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

Источник

Php что такое dto

Data transfer objects with batteries included

php что такое dto php что такое dto php что такое dto

You can install the package via composer:

php что такое dto

We invest a lot of resources into creating best in class open source packages. You can support us by buying one of our paid products.

We highly appreciate you sending us a postcard from your hometown, mentioning which of our package(s) you are using. You’ll find our address on our contact page. We publish all received postcards on our virtual postcard wall.

The goal of this package is to make constructing objects from arrays of (serialized) data as easy as possible. Here’s what a DTO looks like:

You could construct this DTO like so:

Let’s discuss all possibilities one by one.

Constructing a DTO can be done with named arguments. It’s also possible to still use the old array notation. This example is equivalent to the one above.

If a DTO has a property that is another DTO or a DTO collection, the package will take care of automatically casting arrays of data to those DTOs:

You can build your own caster classes, which will take whatever input they are given, and will cast that input to the desired result.

Take a look at the ComplexObject :

And its caster ComplexObjectCaster :

Instead of specifying which caster should be used for each property, you can also define that caster on the target class itself:

It’s possible to define default casters on a DTO class itself. These casters will be used whenever a property with a given type is encountered within the DTO class.

Using custom caster arguments

Any caster can be passed custom arguments, the built-in ArrayCaster implementation is a good example of how this may be used.

Using named arguments when passing input to your caster will help make your code more clear, but they are not required.

Note that the first argument passed to the caster constructor is always the array with type(s) of the value being casted. All other arguments will be the ones passed as extra arguments in the CastWith attribute.

This package doesn’t offer any specific validation functionality, but it does give you a way to build your own validation attributes. For example, NumberBetween is a user-implemented validation attribute:

It works like this under the hood:

You can map a DTO property from a source property with a different name using the #[MapFrom] attribute.

It works with a «dot» notation property name or an index.

Sometimes you also want to map them during the transformation to Array. A typical usecase would be transformation from camel case to snake case. For that you can use the #[MapTo] attribute.

The previous version of this package added the FlexibleDataTransferObject class which allowed you to ignore properties that didn’t exist on the DTO. This behaviour has been changed, all DTOs are flexible now by default, but you can make them strict by using the #[Strict] attribute:

There are also some helper functions provided for working with multiple properties at once.

Note that all() will simply return all properties, while toArray() will cast nested DTOs to arrays as well.

You can chain the except() and only() methods:

It’s important to note that except() and only() are immutable, they won’t change the original data transfer object.

Immutable DTOs and cloning

This package doesn’t force immutable objects since PHP doesn’t support them, but you’re always encouraged to keep your DTOs immutable. To help you, there’s a clone method on every DTO which accepts data to override:

Collections of DTOs

This version removes the DataTransferObjectCollection class. Instead you can use simple casters and your own collection classes.

Here’s an example of casting a collection of DTOs to an array of DTOs:

If you don’t want the redundant typehint, or want extended collection functionality; you could create your own collection classes using any collection implementation. In this example, we use Laravel’s:

Simple arrays of DTOs

Please see CHANGELOG for more information on what has changed recently.

Please see CONTRIBUTING for details.

If you discover any security related issues, please email freek@spatie.be instead of using the issue tracker.

You’re free to use this package, but if it makes it to your production environment we highly appreciate you sending us a postcard from your hometown, mentioning which of our package(s) you are using.

Our address is: Spatie, Kruikstraat 22, 2018 Antwerp, Belgium.

We publish all received postcards on our company website.

Our Arr class contains functions copied from Laravels Arr helper.

The MIT License (MIT). Please see License File for more information.

About

Data transfer objects with batteries included

Источник

Чистим пхпшный код с помощью 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 мы сможем кастомизировать приведение только нужного нам параметра, а все остальные обработает пакет.

Источник

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

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

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.

Источник

Структуры данных в 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 действительно соблюдаются, этот пакет вам поможет.

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

Источник

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

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