how to update symfony2/doctrine entity from a @Groups inclusion policy JMSSerializer deserialized entity

匿名 (未验证) 提交于 2019-12-03 02:14:01

问题:

I'm trying to update symfony2/doctrine entities using JMSSerializer with an @ExclusionPolicy:None @Groups Inclusion Policy.

 * @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;  } 

reference: http://jmsyst.com/libs/serializer/master/reference/annotations

the result for the following record:

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

is serialized using Group inclusion to avoid serializing information I don't need (associations, blobs, etc..) so when I want to update an entity I deserialize only the updated fields of the entity from the JSON.

$foo->setFlagged(true); $data = $serializer->serialize($foo, 'json', SerializationContext::create()->setGroups(array("flag")));  result: {id:1,flagged:true} 

which when passed back to the application deserializes into the entity

$foo = $serializer->deserialize($jsonFoo,'Foo','json');  result: Foo (id:1, name:null, flagged:true, created_by:null) 

The problem is when I try to merge the entity back into the doctrine entity manager:

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

The resulting foo is trying to update excluded properties (name,created_by) with null.

How do I tell JMSSerializer or Doctrine Entity Managers merge that I don't want to overwrite existing properties with null?

回答1:

I found the answer.

$serializer is a service created by the symfony2 integration bundle JMSSerializerBundle.

The default service is jms_serializer.serializer initializes the JMSSerializer with the default Object Constructor UnserializeObjectConstructor and for doctrine I needed to deserialize with the DoctrineObjectConstructor.

because I only use JMSSerializer in the project for serialize/deserialize of doctrine entities, I overwrote JMSSerializerBundle's jms_serializer.object_constructor with the alias of the proper object constructor service.

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

Is there a better way to configure what object constructor the serializer uses?

I also added the proper context to deserialize:

$serializer->deserialize($jsonFoo,'Foo','json', DeserializationContext::create()->setGroups(array('flag')));  result: Foo (id:1, name:'bar', flagged:true ,created_by:123) 

Using the doctrine object constructor, it figures out that I want to find the object and only apply updates to fields provided in $jsonFoo (and the flag group). This totally eliminates the need for doctrines entity manager merge and I can just persist the object properly.

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


回答2:

in addition to @Heyflynn's answer (thanks!), I needed this to work with doctrine_mongodb, so I modified my services.yml as follows:

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 

important fact is the @doctrine_mongodb as argument for jms_serializer.doctrine_object_constructor instead the original doctrine parameter in the bundle's 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:

To use JMS deserializer for MongoDB documents and ORM entities you can use

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 

As you see in jms_serializer.doctrine_object_constructor second argument (fallbackConstructor) is jms_serializer.doctrine_mongodb_object_constructor it means that if your object isn't entity then jms will try to use fallbackConstructor and if your deserialised object isn't Document either then will be used default unserialize_object_constructor

if you deserialize entity

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

if document

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


易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!