Сериализация с Serializer

Команды в контроллерах мы до сих пор заполняли данными из JSON-запросов вручную. Это неудобно. И любое несоответствие типов приведёт к ошибке 500 Server Error.

Вместо этого упростим себе работу и автоматизируем ввод и вывод JSON для наших запросов и команд с помощью компонента Serializer. И сделаем красивый вывод списка ошибок типов при неверных запросах:

  • 00:00:23 Ручное заполнение полей
  • 00:04:06 Использование сериалайзеров
  • 00:05:10 Пакет Symfony Serializer
  • 00:10:45 Установка компонента
  • 00:11:02 Использование в контроллерах
  • 00:13:33 Регистрация в DI-контейнере
  • 00:16:05 Дополнительные нормализаторы
  • 00:18:34 Пользовательские нормалайзеры
  • 00:19:59 Внутреннее устройство
  • 00:23:59 Нормализация в контроллере
  • 00:26:21 Проверка типов ввода
  • 00:28:32 Доработка ClearEmptyInputMiddleware
  • 00:30:37 Возврат 400 Bad Request
  • 00:31:44 Единообразие с ошибками валидации
  • 00:33:55 Вывод нескольких ошибок
  • 00:36:14 Преобразование в исключение валидации
  • 00:38:42 Вынос обработчиков в Middleware
  • 00:43:03 Одиночные и множественные
  • 00:45:49 Контроль наличия полей
  • 00:52:37 Рефакторинг Middleware
  • 00:54:26 Создание адаптеров
  • 00:58:23 Типизация с Generics
  • 01:00:58 Заполнение через конструктор
  • 01:02:23 Адаптер для Serializer
  • 01:04:18 Риски использования при выводе
  • 01:06:17 Кастомизация ответа
  • 01:11:17 Проверка результата
Скрытый контент (код, слайды, ...) для подписчиков. Открыть →
Дмитрий Елисеев
elisdn.ru
Комментарии (19)
Максим (@myks92)
2022-08-20 12:25

Ура! Подходим стремительными темпами ближе к домену. Выходы стали чаще! Супер! Так держать))

Ответить
Руслан
2022-08-20 15:19

Спасибо!

Ответить
Владимир Перепеченко
2022-08-22 21:33

Благодарю за детальность, Дмитрий!

Для меня очень своевременно, так как я сам набрел на Symfony Serializer за три дня до выхода эпизода.

Традиционный вопрос чайника:

Вы сконфигурировали сериалайзер для десиарилизации (объект->string) используя несколько отдельных нормализаторов:

  • DateTimeNormalizer
  • PropertyNormalizer
return new Serializer([
    ...$normalizers,
    new DateTimeNormalizer(),
    new PropertyNormalizer(
        classMetadataFactory: new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader())),
        propertyTypeExtractor: new PropertyInfoExtractor(
            typeExtractors: [
                new PhpDocExtractor(),
                new ReflectionExtractor(),
            ]
        )
    ),
    new ArrayDenormalizer(),
], [
    new JsonEncoder(),
]);

и создали непонятные для меня вспомогательные классы Denormalizer, Normalizer.

А так как в моем проекте я собираюсь десереализовывать DTO классы как со свойствами, так и с геттерами/сеттерами, то одного PropertyNormalizer будет недостаточно.

Поэтому вместо набора частных нормалайзеров, я объектов я использовал (больше интуитивно) один могучий объект ObjectNormalizer:

MyObjectSerializer::class => static function (): Serializer {
    $normalizers = [
        new ObjectNormalizer(
            null,
            null,
            null,
            new ReflectionExtractor()
        )
    ];

    $encoders = new JsonEncoder();

    return new Serializer([
        ...$normalizers,
        // new GetSetMethodNormalizer(), 
        new ArrayDenormalizer(),
    ], [
        $encoders
    ]);
},

И свое дело он делает: преобразует json в мой кастомный DTO объект. Причем рекурсивно десериализует сложные вложенные объекты из json во вложенные DTO объекты ( У меня сложная форма с данными из разных таблиц и модулей)

Исходя из этого, мучаюсь вопросами:

1) Для описанного выше функционала (десериализация сложных структур в сложные объекты) , есть ли равенство в использовании сериалайзера собранного

из моего набора: ObjectNormalizer + ReflectionExtractor

и

из Вашго набора: PropertyNormalizer + ArrayDenormalizer + для моего функционала, видимо, придется добавить GetSetMethodNormalizer

На взгляд чайника - мой вариант лаконичнее.

2) также, пока не могу придумать практического применения для массивов, которые Вы используете как временные промежуточные объекты между строками и объектами в процессах serialize/deserialize. Ведь навскидку проще реализовать основную задачу (преобразовывать данные json->object/object->json) напрямую, используя ObjectNormalizer без массивов посредников.

В чём мои заблуждения?

Желаю процветать и здравствовать!

Ответить
Дмитрий Елисеев
2022-09-20 07:43

есть ли равенство в использовании сериалайзера собранного из моего набора и вашего набора

Да, если нужны геттеры и сеттеры, тогда лучше взять ObjectNormalizer.
Если не нужны, то тогда использовать PropertyNormalizer.

Ответить
Дмитрий Елисеев
2022-09-20 07:48

Проще реализовать основную задачу (преобразовывать данные json->object/object->json) напрямую.

Поэтому в видео и показаны оба варианта, что можно так и так.

Ответить
Владимир Перепеченко
2022-08-23 05:41

Дмитрий, с Вашей конфигурацией сериалайзера, моя десериализация прошла - как задумано. Значит мой с ObjectNormalizer просто неверно сконфигурирован.

Ответить
Arunas
2022-08-28 16:36

спасибо

Ответить
Dzianis
2022-10-15 19:43

Спасибо, Дмитрий.

Вот я и добрался до 72-го видео. Всё, очень подробно, супер!

Есть понимание когда будут опубликованы следующие видео?

Ответить
Дмитрий
2022-10-26 12:52

Можно пойти дальше. Экшены принимают сразу в параметрах команды и возвращают объекты. Тогда будет меньше болерплейт кода в экшенах.

Ответить
Андрей
2022-11-14 14:58

Я бы предложил дтошки респонсов реквестов генерить через свагер, а для очереди использовать протобаф описание. Спасибо за ролик как всегда почерпнул для себя какие то нюансы.

Ответить
Дмитрий Елисеев
2022-12-01 14:49

Да, если сначала придумывают схему API, то удобнее сделать файл со схемой и по нему генерировать DTO для бэкенда и фронтенда.

Ответить
Aёct'ann
2022-12-14 19:54

Есть, 72-й урок пройден) Спасибо за материал, пойду смотреть другие скринкасты.

Ответить
Sergei
2023-01-08 16:01
$obj = new \stdClass();
$obj->lol = 'kek';

$serializer = new Serializer([
    new PropertyNormalizer(),
], [
    new XmlEncoder(),
    new JsonEncoder()
]);

$data = $serializer->normalize($obj, 'json');

Я взял дефолтные енкодеры и нормалайзер. Я ожидал, что в $data придет JSON, но почему то сконвертилось в массив. Оно вообще независимо от параметра формата конвертит все в массив. Это так и должно быть? Спасибо.

Ответить
Дмитрий Елисеев
2023-01-10 14:10

Да, всё так:

  • метод normalize конвертирует объект в массив
  • метод encode собирает из массива JSON
  • метод serialize вызывая normalize и encode перегоняет объект в JSON

Так что вместо normalize вызывайте serialize.

Ответить
Руслан
2023-02-10 13:05

Здравствуйте, когда примерно будет продолжение серии slim/react?

Ответить
Михаил
2023-02-15 15:59

Переводы бы еще добавить для этих ошибок

Ответить
Дмитрий Елисеев
2023-05-23 17:31

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

Ответить
Руслан
2023-07-29 17:08

Дмитрий, сможете подсказать как обновить Serializer на использование symfony/serializer 6.3?

Ответить
Дмитрий Елисеев
2023-08-24 14:38

Когда вернёмся к коду все компоненты обновим.

Ответить
Зарегистрируйтесь или войдите чтобы оставить комментарий

Или войти через:

Yandex
MailRu
GitHub
Google