Группы сериализации JMSSerializerBundle в сущностях с отношениями

У меня проблема с сериализацией объекта со многими отношениями с использованием групп. У меня проблема с сериализацией связанных сущностей таким образом.

Допустим, у меня есть две сущности: продукт и связанный с ним элемент.

/**
 *
 * @Serializer\ExclusionPolicy("none")
 */
class Product {

    /**
     * Primary key
     * @var integer $id
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     * 
     * @Serializer\Groups({"list","details"})
     * @Serializer\Type("integer")
     */
    protected $id;

    /**
     * @Serializer\Groups({"list","details"})
     * @Serializer\Type("string")
     */
    protected $name;

    /**
     * @ORM\Column(name="description", type="string", length=4096, nullable=true)
     * 
     * @Serializer\Groups({"details"})
     * @Serializer\Type("string")
     */
    protected $description;

    /**
     * @var ArrayCollection
     * 
     * @ORM\OneToMany(targetEntity="Madden\ProjectBundle\Entity\ProjectResource", mappedBy="project")
     * @Serializer\Groups({"details"})
     * @Serializer\Type("ArrayCollection<Element>")
     */
    protected $details1;

    /**
     * Relation to project tasks
     * @ORM\OneToMany(targetEntity="Madden\ProjectBundle\Entity\ProjectTask", mappedBy="project")
     * @Serializer\Exclude()
     * @Serializer\Type("ArrayCollection<Element>")
     */
    protected $details2;

    ...

}

Сущность элемента имеет аналогичную структуру:

/**
 *
 * @Serializer\ExclusionPolicy("none")
 */
class Element {

    /**
     * Primary key
     * @var integer $id
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     * 
     * @Serializer\Groups({"list","details"})
     * @Serializer\Type("integer")
     */
    protected $id;

    /**
     * @Serializer\Groups({"list","details"})
     * @Serializer\Type("string")
     */
    protected $name;

    /**
     * @ORM\Column(name="description", type="string", length=4096, nullable=true)
     * 
     * @Serializer\Groups({"details"})
     * @Serializer\Type("string")
     */
    protected $description;

    ...
}

Моя проблема заключается в том, что когда я сериализую продукт с групповым объектом «детали», я хочу сериализовать только идентификаторы элементов, но, как вы видите, объект определил те же группы, что и продукт (в случае, если мне понадобятся сведения об объекте элемента), потому что я хочу иметь унифицированные группы для всех моих объектов и предотвратить создание сотен групп, таких как «product_details», «element_details» и т. д.

Есть ли способ в конечном итоге изменить группу сериализации, когда я посещаю отношение или что-то в этом роде? Может обработчик или что-то в этом роде?

С уважением и благодарностью за любую помощь


person mrMantir    schedule 05.10.2012    source источник
comment
Я использовал решение product_detail/product_list и т. д., и это было довольно приятно, потому что у вас всегда есть полный контроль над сериализацией. Недостатком, очевидно, является то, насколько многословным становится код при сериализации нескольких классов... Я бы также использовал xxx_partial/xxx_full вместо xxx_list/xxx_details.   -  person AdrienBrault    schedule 05.10.2012
comment
@AdrienBrault Спасибо за ответ. Да, я использую это решение прямо сейчас, но у него есть недостаток - эти группы должны быть определены в каждом связанном объекте, если у меня много много объектов в часто используемых словарях объектов, аннотация групп объектов будет огромной   -  person mrMantir    schedule 06.10.2012
comment
Это насчет VirtualProperty?   -  person Alexey B.    schedule 11.10.2013
comment
именно VirtualProperty решила мою проблему, чтобы включить идентификаторы отношений в сериализованные объекты. Но это только частичное решение этой проблемы, поскольку могут быть сценарии, в которых вы хотите вложить связанные объекты по соображениям производительности.   -  person Denes Papp    schedule 16.10.2013


Ответы (3)


К сожалению, на самом деле вы не можете (но продолжайте читать ;-)), по крайней мере, не без изменений в библиотеке сериализатора. Виновником является то, что список групп фиксируется в пределах GroupExclusionStrategy (на который ссылается Context) в ту минуту, когда вы запускаете процесс сериализации. На самом деле в коде есть утверждение, которое предотвращает изменение стратегии исключения после запуска (де-)сериализации.

Но так получилось, что у меня была точно такая же проблема в моем проекте, и я внес необходимые изменения в код сериализатора. Я немного подчистил код и загрузил его на Github (https://github.com/andreasferber/serializer/tree/recursion-groups).

Он добавляет новые метаданные свойств, с помощью которых вы можете добавлять, удалять или переопределять группы при переходе к подобъектам. С аннотациями это выглядит так:

/**
 * @Serializer\RecursionGroups(set={"foo", "bar"}, add={"baz"}, remove={"Default"})
 */
private $myProperty;

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

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

person aferber    schedule 15.10.2013
comment
на первый взгляд вроде неплохо, сегодня-завтра попробую - person Denes Papp; 16.10.2013
comment
ну, ИМХО, разветвление официального сериализатора не должно быть лучшим общим решением этой распространенной проблемы, но пока не найдено достаточно хорошего решения, кроме использования решения VirtualProperty - person Denes Papp; 18.10.2013
comment
Вы должны рассмотреть запрос на вытягивание вверх по течению - person Szpadel; 09.03.2014
comment
@aferber Это выглядит действительно полезно. Есть ли шанс получить PR, чтобы попытаться получить это в мастер? - person lopsided; 24.02.2015
comment
Это функция, которой в настоящее время катастрофически не хватает сериализатору, и вам следует выбрать MR. Я, например, хотел бы видеть это в компоненте. - person Zephyr; 17.07.2017

Решение для этого фактически описано в официальная документация.

При этом решение, предложенное @aferber, кажется лучше во многих отношениях: проще в обслуживании, менее многословно, более гибко...

person Zephyr    schedule 17.07.2017
comment
по какой-то глупой причине это решение в официальной документации не работает - person Next Developer; 05.12.2017

Вам нужно использовать setGroups.

Суффикс _group, используемый в официальной документации, не нужен.

$context->setGroups([
    'Default', //if you want

    // use this linked entity but show only its id
    'group_of_linked_field',
    'group_of_linked_field' => [
        'id' // you will need to define this group first
    ],

    // use this linked entity and show fields as described
    'group_of_other_linked_field',
    'group_of_other_linked_field' => [
        // just as an example
        'Default',
        'details',
    ],
]);

Это не работает с addGroup или addGroups! Оба они не будут принимать ассоциативные массивы. setGroups — ваше (единственное?) решение.

person auipga    schedule 11.06.2020