2017-02-23 55 views
0

使用Symfony 3我寫了一個應該通過JSON/JMSSerializer公開的實體(以RESTful方式)。它看起來像這樣:JMSSerializer:使用ID從數據庫中檢索相關對象

/** 
* MainEntity 
* 
* @ORM\Table(name="MainEntity") 
* @ORM\Entity 
* 
*/ 
class MainEntity{ 

    /** 
    * @var integer 
    * 
    * @ORM\Column(name="id", type="integer", nullable=false) 
    * @ORM\Id 
    * @ORM\GeneratedValue(strategy="IDENTITY") 
    */ 
    private $id; 

    //... some more "simple" fields ... 

    /** 
    * @ORM\ManyToOne(targetEntity="SubEntity") 
    * @ORM\JoinColumns({ 
    * @ORM\JoinColumn(name="subentity", referencedColumnName="id") 
    * }) 
    * @JMS\Accessor(getter="getSubEntityId",setter="setSubEntity") 
    * @JMS\Type("integer") 
    * @JMS\SerializedName("subEntityId") 
    */ 
    private $subEntity; 
    //... 

    public function getSubEntityId() { 
     return $this->subEntity->getId(); 
    } 

} 

整個事情的JSON序列化就像一個魅力!尤其是,只有子實體ID纔會暴露,而不是整個子實體,這可能相當大。

所以不是:

{"id": 1, ..., "subEntity": {"id": 123, "name": "Great subEntity", ...} } 

我得到

{"id": 1, ..., "subEntityId": 123 } 

這是完全是我啃老族。

但是,當涉及到系列化,我遇到麻煩......當然我還需要使用縮短的JSON格式的傳入的請求,但這種失敗,因爲setSubEntity需要一個SubEntity實例,而不是一個號碼。

有沒有什麼辦法可以實現反序列化來檢索相關對象ID時給出?

我想過這些可能性:

  1. 使用一個特殊的制定者(在@JMS\Accessor註釋給出),採取id和填充從數據庫中檢索對象的subEntity場。但是這意味着將EntityManager注入實體(或類似的和不好的...)
  2. 添加一個新的數字字段subEntityId並讓它由一個特殊的setter(如上圖)填充。然後使用控制器讀取它,從數據庫中獲取SubEntity對象,並在之後使用setSubEntity方法實體被反序列化。對我來說也不好看......

有什麼建議嗎?我看到,在Stackoverflow上有一些類似的問題,但在我看來,沒有人描述我的特例。

非常感謝提前!

回答

1

其實你不不需要創建額外的字段或設置器。可能的方法是創建自己的Serializer handler並將其用於映射。

我已經回答了in this topic,所以你可以在那裏得到代碼示例。

+0

非常有幫助!謝謝! – ahuemmer

0

添加到您的服務配置,在這裏我把它寫在YAML

jms_serializer.object_constructor: 
    alias: jms_serializer.initialized_object_constructor 
    public: false 

jms_serializer.initialized_object_constructor: 
    class: MyApp\Bundle\CoreBundle\Serializer\InitializedObjectConstructor 
    arguments: ["@jms_serializer.unserialize_object_constructor"] 

jms_serializer.initialized_object_constructor

<?php 
namespace MyApp\Bundle\CoreBundle\Serializer; 
use JMS\Serializer\VisitorInterface; 
use JMS\Serializer\Metadata\ClassMetadata; 
use JMS\Serializer\DeserializationContext; 
use JMS\Serializer\Construction\ObjectConstructorInterface; 
class InitializedObjectConstructor implements ObjectConstructorInterface 
{ 
    private $fallbackConstructor; 
    public function __construct(ObjectConstructorInterface $fallbackConstructor) 
    { 
     $this->fallbackConstructor = $fallbackConstructor; 
    } 
    public function construct(VisitorInterface $visitor, ClassMetadata $metadata, $data, array $type, DeserializationContext $context) 
    { 
     if ($context->attributes->containsKey('target') && $context->getDepth() === 1) { 
      return $context->attributes->get('target')->get(); 
     } 
     return $this->fallbackConstructor->construct($visitor, $metadata, $data, $type, $context); 
    } 
} 

添加這個類在你的控制器:

protected function flushRequestData(Request $request, $entity = null) 
{ 
    $data = $request->getContent(); 
    $dm = $this->get('doctrine_mongodb.odm.default_document_manager'); 
    $context = new DeserializationContext(); 
    if ($entity) { 
     $context->attributes->set('target', $entity); 
    } 

    $deserializedObj = $this->get('serializer')->deserialize(
     $data, 
     $this->getRepository()->getClassName(), 
     'json', 
     $context 
    ); 

    // After deserialized into your entity you need to manually set each of the related entity one by one manually 
}