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

Я пытаюсь обновить сущности symfony2/doctrine, используя JMSSerializer с политикой включения @ExclusionPolicy:None @Groups.

 * @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) с помощью null.

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


person Heyflynn    schedule 13.05.2013    source источник
comment
единственный вариант, который я нашел далеко stackoverflow.com/questions/8726611/, что означает обход десериализации JMSSerializers и проверку/обновление объекта вручную (в этом примере пример обхода сеттеров).   -  person Heyflynn    schedule 13.05.2013


Ответы (3)


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

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

Служба по умолчанию jms_serializer.serializer инициализирует JMSSerializer конструктором объектов по умолчанию 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)

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

$em->persist($foo);
$em->flush();
person Heyflynn    schedule 13.05.2013

в дополнение к ответу @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" />
person con    schedule 26.06.2013

Чтобы использовать десериализатор 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, а если ваш десериализованный объект не является Document, то будет использоваться значение по умолчанию unserialize_object_constructor

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

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

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

$dm->persist($foo);
$dm->flush();
person Anatoly E    schedule 28.07.2017