Symfony десериализует вложенные объекты

Я использовал сериализатор Symfony для сериализации моего объекта Recherche. В объекте Recherche у меня есть подобъекты: Categorie и Lieu.

Когда я десериализую свой объект Recherche, все подобъекты преобразуются в массивы. Я бы хотел, чтобы они снова стали объектами.

Вот как я сериализовал свой объект:

$encoders = array(new JsonEncoder());
$normalizer = new ObjectNormalizer();
$normalizer->setIgnoredAttributes(array('parent', 'enfants'));
$normalizer->setCircularReferenceHandler(function ($object) {
    return $object->getCode();
});
$normalizers = array($normalizer);
$serializer = new Serializer($normalizers, $encoders);
$rechercheJson= $serializer->serialize($recherche, 'json');

И вот как я его десериализую:

$encoders = array(new JsonEncoder());
$normalizer = new ObjectNormalizer();
$normalizer->setIgnoredAttributes(array('parent', 'enfants'));
$normalizer->setCircularReferenceHandler(function ($object) {
    return $object->getCode();
});
$normalizers = array($normalizer);
$serializer = new Serializer($normalizers, $encoders);
$recherche = $serializer->deserialize($recherche_json, Recherche::class, 'json');

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

У кого-нибудь есть идея помочь?

Спасибо !

РЕДАКТИРОВАТЬ : Увидев этот пост: Денормализация вложенной структуры в объектах с помощью сериализатора Symfony 2

Я пробовал это:

$encoders = array(new JsonEncoder());
            $normalizer = new ObjectNormalizer(null, null, null, new SerializationPropertyTypeExtractor());
            $normalizer->setIgnoredAttributes(array('parent', 'enfants'));
            $normalizer->setCircularReferenceHandler(function ($object) {
                return $object->getCode();
            });
            $normalizers = array($normalizer,  new ArrayDenormalizer());
            $serializer = new Serializer($normalizers, $encoders);
            $recherche = $serializer->deserialize($recherche_json, Recherche::class, 'json');

И SerializationPropertyTypeExtractor:

class SerializationPropertyTypeExtractor implements PropertyTypeExtractorInterface {
    /**
     * {@inheritdoc}
     */
    public function getTypes($class, $property, array $context = array())
    {
        if (!is_a($class, Recherche::class, true)) {
            return null;
        }

        if ('make' !== $property) {
            return null;
        }

        if ('lieu' === $property)
        {
            return [new Type(Type::BUILTIN_TYPE_OBJECT, true, LieuRecherche::class)];
        }
        if ('categorie' === $property)
        {
            return [new Type(Type::BUILTIN_TYPE_OBJECT, true, Categorie::class)];
        }

        return null;
    }
}

И это хорошо работает!

Я обычно использую шаблон стратегии с компонентом сериализатора Symfony. Он позволяет вам точно определить, как должен выглядеть нормализованный и денормализованный объект / массив. Я пришлю пример чуть позже.

Edward 11.04.2018 17:27

Да, пример может быть хорошим, я не понимаю :-)

wyllyjon 11.04.2018 17:39
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
2
7 649
2

Ответы 2

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

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

./src/Denormalizer

<?php

declare(strict_types=1);

namespace App\Denormalizer;

use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
use Symfony\Component\Serializer\Exception\CircularReferenceException;
use Symfony\Component\Serializer\Exception\InvalidArgumentException;
use Symfony\Component\Serializer\Exception\LogicException;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;

/**
 * Class ConfigDenormalizer
 * @package App\Denormalizer
 */
class ConfigDenormalizer implements DenormalizerInterface
{

    /**
     * @param mixed $data
     * @param string $class
     * @param null $format
     * @param array $context
     * @return array
     */
    public function denormalize($data = [], $class, $format = null, array $context = array())
    {
        $result = [];
        foreach($data as $key => $config) {
            $result[ $config['attribute'] ] = $config['valorem'];
        }
        return $result;
    }

    /**
     * @param mixed $data
     * @param string $type
     * @param null $format
     * @return bool
     */
    public function supportsDenormalization($data, $type, $format = null)
    {
        return $type === self::class;
    }
}

./src/Normalizer

<?php

declare(strict_types=1);

namespace App\Normalizer;

use Symfony\Component\Form\Form;
use Symfony\Component\Serializer\Exception\CircularReferenceException;
use Symfony\Component\Serializer\Exception\InvalidArgumentException;
use Symfony\Component\Serializer\Exception\LogicException;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;

/**
 * Class WidgetConfigNormalizer
 * @package App\Normalizer
 */
class WidgetAttributeNormalizer implements NormalizerInterface
{
    /**
     * @param object $form
     * @param null $format
     * @param array $context
     * @return array
     */
    public function normalize($form, $format = null, array $context = array()): array
    {
        $result = [];

        foreach($form->getData() as $key => $value) {
            $result[] = [
                'attribute' => (string) $key,
                'valorem' => (string) $value,
            ];
        }

        return $result;
    }

    /**
     * @param mixed $form
     * @param null $format
     * @return bool
     */
    public function supportsNormalization($form, $format = null)
    {
        if ($form instanceof Form) {
            return $form->getName() === 'widgetAttribute';
        }
    }

}

и вы бы назвали это так:

//denormalize
$this->denormalizer->denormalize(
    json_decode($response->getContent(), true),
    ConfigDenormalizer::class
);

//normalize
$form = $this->formFactory->create(myConfigFormType::class);
$form->submit($data);
$this->normalizer->normalize($form);

Или, если вы хотите использовать сериализатор (обратите внимание, нам не нужен json_decode):

//deserialize
$array = $this->serializer->deserialize(
    $response->getContent(),
    ConfigDenormalizer::class,
    'json'
);

//serialize
$json = $this->serialize->serialize($form, 'json');

Конечно, в вашем денормализаторе вы можете преобразовать свой массив в простой старый объект (сущность) php. или просто выведите массив, выбор за вами.

Таким образом, все, что вам нужно сделать, это ввести SerializerInterface в ваши контроллеры следующим образом:

use Symfony\Component\Serializer\SerializerInterface;

class EmbedController extends Controller
{
    /**
    * @var SerializerInterface
    */
    private $serializer;

    public function __construct(SerializerInterface $serializer)
    {
        $this->serializer = $serializer;
    }

}

Этот метод значительно упрощает модульное тестирование, поскольку все отделено, поэтому его можно смоделировать. :)

Также стоит проверить документацию: https://symfony.com/doc/current/components/serializer.html

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

Надеюсь это поможет.

Если вы используете Autowiring, вам не нужно определять свои (де) нормализаторы как службы или помечать их, это должно работать сразу из коробки. раз уж вы сделали composer require serializer;)

Edward 11.04.2018 18:08

Спасибо, Эдвард. Если я хорошо понимаю, ты ведь делаешь почти всю работу сам? Сериализатор Symfony почти бесполезен, не так ли?

wyllyjon 12.04.2018 09:57

Вам решать, если вы хотите сериализовать или десериализовать без настраиваемых (де) нормализаторов, вы используете службу сериализатора напрямую $json = $this->serializer->serialize($object, 'json');, но в вашем вопросе вам нужна настраиваемая структура, для этого вам нужны настраиваемые (де) нормализаторы.

Edward 12.04.2018 18:43

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

wyllyjon 13.04.2018 10:04

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

Я нашел лучшее решение, используя PhpDocExtractor и ReflectionExtractor, которые извлекают для вас информацию о свойствах.

$encoder = [new JsonEncoder()];
$extractor = new PropertyInfoExtractor([], [new PhpDocExtractor(), new ReflectionExtractor()]);
$normalizer = [new ArrayDenormalizer(), new ObjectNormalizer(null, null, null, $extractor)];
$serializer = new Serializer($normalizer, $encoder);
$result = $serializer->deserialize($data,someEntity::class,'json');

Это делает всю работу за меня. Надеюсь, это кому-то поможет.

Другие вопросы по теме