Как обновить объект symfony2/doctrine из политики включения @Groups JESSerializer десериализованный объект

Я пытаюсь обновить объекты symfony2/doctrine с помощью JMSSerializer с помощью политики @ExclusionPolicy: None @Groups Inclusion.

 * @Serializer\ExclusionPolicy("none")
 */
 class Foo
 {
    /**
     * @Serializer\Groups({"flag","edit"})
     */
    protected $id;

    /**
     * @Serializer\Groups({"edit"})
     */
    protected $name;

    /**
     * @Serializer\Groups({"flag"})
     */
    protected $flag;

    /**
     * @Serializer\Exclude()
     */
    protected $createdBy;
 }

ссылка: http://jmsyst.com/libs/serializer/master/reference/annotations

результат для следующей записи:

Foo (id:1, name:'bar', flagged:false ,created_by:123)

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

$foo->setFlagged(true);
$data = $serializer->serialize($foo, 'json', SerializationContext::create()->setGroups(array("flag")));

result:
{id:1,flagged:true}

который при обращении к приложению десериализуется в сущность

$foo = $serializer->deserialize($jsonFoo,'Foo','json');

result:
Foo (id:1, name:null, flagged:true, created_by:null)

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

$foo = $em->merge($foo);
$em->persist($foo);
$em->flush();

В результате foo пытается обновить исключенные свойства (name, created_by) с нулевым значением.

Как сообщить JMSSerializer или Doctrine Entity Managers слияние, что я не хочу перезаписывать существующие свойства с нулевым значением?

Ответ 1

Я нашел ответ.

$serializer - это сервис, созданный пакетом интеграции symfony2 JMSSerializerBundle.

Служба по умолчанию jms_serializer.serializer инициализирует JMSSerializer с помощью Object Constructor по умолчанию UnserializeObjectConstructor, а для доктрины мне нужно десериализовать с помощью DoctrineObjectConstructor.

потому что я использую только JMSSerializer в проекте для сериализации/десериализации сущности доктрины, я перезаписал JMSSerializerBundle jms_serializer.object_constructor псевдоним правильной службы конструктора объектов.

<service id="jms_serializer.object_constructor" alias="jms_serializer.doctrine_object_constructor" public="false"/>

Есть ли лучший способ настройки конструктора объектов, который использует сериализатор?

Я также добавил правильный контекст для десериализации:

$serializer->deserialize($jsonFoo,'Foo','json', DeserializationContext::create()->setGroups(array('flag')));

result:
Foo (id:1, name:'bar', flagged:true ,created_by:123)

Используя конструктор объекта doctrine, выясняется, что я хочу найти объект и применять обновления только к полям, указанным в $jsonFoo (и группе флагов). Это полностью исключает необходимость объединения объединений сущностей доктрин, и я могу просто сохранить объект должным образом.

$em->persist($foo);
$em->flush();

Ответ 2

в дополнение к @Heyflynn ответ (спасибо!), мне это нужно для работы с doctrine_mongodb, поэтому я изменил свой services.yml следующим образом:

services:
    jms_serializer.doctrine_object_constructor:
        class:        %jms_serializer.doctrine_object_constructor.class%
        public:       false
        arguments:    ["@doctrine_mongodb", "@jms_serializer.unserialize_object_constructor"]

    jms_serializer.object_constructor:
        alias: jms_serializer.doctrine_object_constructor

Важным фактом является @doctrine_mongodb как аргумент для jms_serializer.doctrine_object_constructor вместо исходного doctrine параметра в services.xml:

    <service id="jms_serializer.doctrine_object_constructor" class="%jms_serializer.doctrine_object_constructor.class%" public="false">
        <argument type="service" id="doctrine"/>
        <argument type="service" id="jms_serializer.unserialize_object_constructor"/>
    </service>
    <service id="jms_serializer.unserialize_object_constructor" class="%jms_serializer.unserialize_object_constructor.class%" public="false" />
    <service id="jms_serializer.object_constructor" alias="jms_serializer.unserialize_object_constructor" public="false" />

Ответ 3

Чтобы использовать десериализатор JMS для документов MongoDB и объектов ORM, вы можете использовать

jms_serializer.doctrine_mongodb_object_constructor:
    class:        %jms_serializer.doctrine_object_constructor.class%
    public:       false
    arguments:    ["@doctrine_mongodb", "@jms_serializer.unserialize_object_constructor"]

jms_serializer.doctrine_object_constructor:
    class:        %jms_serializer.doctrine_object_constructor.class%
    public:       false
    arguments:    ["@doctrine", "@jms_serializer.doctrine_mongodb_object_constructor"]

jms_serializer.object_constructor:
    alias: jms_serializer.doctrine_object_constructor
    public: false

Как вы видите в jms_serializer.doctrine_object_constructor второй аргумент (fallbackConstructor) равен jms_serializer.doctrine_mongodb_object_constructor, это означает, что если ваш объект не является сущностью, тогда jms попытается использовать fallbackConstructor, и если ваш десериализованный объект не является Документом, то он будет использоваться default unserialize_object_constructor

если вы десериализуете сущность

$em->persist($foo);
$em->flush();

если документ

$dm->persist($foo);
$dm->flush();