如何从序列化的JSON更新Doctrine实体?

Rob*_*rto 8 symfony doctrine-orm

我们正在使用Symfony2来创建API.更新记录时,我们希望JSON输入表示序列化更新的实体.JSON数据将不包含某些字段(例如,创建实体时,CreatedAt只应设置一次 - 并且永远不会更新).例如,这是一个示例JSON PUT请求:

{"id":"1","name":"anyname","description":"anydescription"}
Run Code Online (Sandbox Code Playgroud)

这是Controller上的PHP代码,它应该根据上面的JSON更新实体(我们使用的是JMS序列化器Bundle):

$supplier = $serializer->deserialize(
    $this->get('request')->getContent(),
    'WhateverEntity',
    'json'
);
Run Code Online (Sandbox Code Playgroud)

EntityManger(正确地)理解这是一个更新请求(实际上,隐式触发了SELECT查询).EntityManager还猜测(不正确)CreatedAt属性应该被NULL化 - 它应该保留前一个属性.

如何解决这个问题?

Hey*_*ynn 14

使用JMSSerializerBundle按照http://jmsyst.com/bundles/JMSSerializerBundle上的安装说明进行操作

要么创建自己的序列化程序服务,要么更改JMSSerializerBundle以使用doctrine对象构造函数而不是简单对象构造函数.

<service id="jms_serializer.object_constructor" alias="jms_serializer.doctrine_object_constructor" public="false"/>
Run Code Online (Sandbox Code Playgroud)

这基本上可以处理Ocramius解决方案的功能,但使用JMSSerializerBundles反序列化.


Ocr*_*ius 9

我会使用Doctrine\ORM\Mapping\ClassMetadataAPI来发现实体中的现有字段.您可以执行以下操作(我不知道JMSSerializerBundle如何工作):

//Unserialize data into $data
$metadata = $em->getMetadataFactory()->getMetadataFor($FQCN);
$id = array();
foreach ($metadata->getIdentifierFieldNames() as $identifier) {
    if (!isset($data[$identifier])) {
        throw new InvalidArgumentException('Missing identifier');
    }
    $id[$identifier] = $data[$identifier];
    unset($data[$identifier]);
}
$entity = $em->find($metadata->getName(), $id);
foreach ($metadata->getFieldNames() as $field) {
    //add necessary checks about field read/write operation feasibility here
    if (isset($data[$field])) {
        //careful! setters are not being called! Inflection is up to you if you need it!
        $metadata->setFieldValue($entity, $field, $data[$field]);
    }
}
$em->flush();
Run Code Online (Sandbox Code Playgroud)


Tom*_*asz 5

也可以通过使用object_to_populate选项的Symfony序列化程序来实现。

示例:我收到JSON请求。如果记录存在于数据库中,我想更新正文中收到的字段,如果不存在,我想创建一个新记录。

/**
 * @Route("/{id}", methods={"PUT"})
 */
public function upsert(string $id, Request $request, SerializerInterface $serializer)
{
  $content = $request->getContent(); // Get json from request

  $product = $this->getDoctrine()->getRepository(Product::class)->findOne($id); // Try to find product in database with provided id

  if (!$product) { // If product does not exist, create fresh entity
      $product = new Product();
  }

  $product = $serializer->deserialize(
            $content,
            Product::class,
            'json',
            ['object_to_populate' => $product] // Populate deserialized JSON content into existing/new entity
        );
  // validation, etc...
  $this->getDoctrine()->getManager()->persist($product); // Will produce update/instert statement 
  $this->getDoctrine()->getManager()->flush($product);

// (...)
Run Code Online (Sandbox Code Playgroud)