Сериализация с 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)

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

Ответить
Руслан

Спасибо!

Ответить
Владимир Перепеченко

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

Для меня очень своевременно, так как я сам набрел на 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 без массивов посредников.

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

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

Ответить
Дмитрий Елисеев

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

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

Ответить
Дмитрий Елисеев

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

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

Ответить
Владимир Перепеченко

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

Ответить
Arunas

спасибо

Ответить
Dzianis

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

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

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

Ответить
Дмитрий

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

Ответить
Андрей

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

Ответить
Дмитрий Елисеев

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

Ответить
Aёct'ann

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

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

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

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

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

Ответить
Дмитрий Елисеев

Да, всё так:

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

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

Ответить
Руслан

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

Ответить
Михаил

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

Ответить
Дмитрий Елисеев

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

Ответить
Руслан

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

Ответить
Дмитрий Елисеев

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

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

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

Yandex
MailRu
GitHub
Google