【问题标题】:How to update a Doctrine Entity from a serialized JSON?如何从序列化的 JSON 更新 Doctrine 实体?
【发布时间】:2012-01-04 12:06:43
【问题描述】:

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

{"id":"1","name":"anyname","description":"anydescription"}

这是控制器上的 PHP 代码,它应该根据上面的 JSON 更新实体(我们正在使用 JMS 序列化程序包):

$supplier = $serializer->deserialize(
    $this->get('request')->getContent(),
    'WhateverEntity',
    'json'
);

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

如何解决这个问题?

【问题讨论】:

    标签: symfony doctrine-orm


    【解决方案1】:

    使用 JMSSerializerBundle 遵循安装说明 http://jmsyst.com/bundles/JMSSerializerBundle

    创建您自己的序列化程序服务或更改 JMSSerializerBundle 以使用教义对象构造函数而不是简单对象构造函数。

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

    这基本上处理了 Ocramius 解决方案所做的,但使用 JMSSerializerBundles 反序列化。

    【讨论】:

    • 我真的无法在任何地方记录如何使用您建议的方法。你能建议我在哪里可以读到它吗?
    • 看起来他们把它移到了它自己的部分下。 configuration
    【解决方案2】:

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

    示例:我收到 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);
    
    // (...)
    

    【讨论】:

    • 有办法填充更多对象吗?对于像这样的请求:{"id":"1","name":"anyname", "otherObject": { "id": 3},} 我想填充主对象中的其他对象,所以 symfony 应该通过 id 从数据库中获取两个不同的对象
    • 是的,正确创建主对象(本例中为 $product)但不初始化 otherObject。由于该教义尝试创建新对象或使现有元素无效。
    【解决方案3】:

    我会使用Doctrine\ORM\Mapping\ClassMetadata API 来发现您实体中的现有字段。 您可以执行以下操作(我不知道 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();
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-05-26
      • 1970-01-01
      • 2011-11-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多