2013-04-13 56 views
25

我正在嘗試使用JMS序列化器序列化一個實體關係。僅使用JMS序列化器將實體關係序列化到ID

這裏是實體:

class Ad 
{ 

    /** 
    * @Type("string") 
    * @Groups({"manage"}) 
    * 
    * @var string 
    */ 
    private $description; 

    /** 
    * @Type("Acme\SearchBundle\Entity\Country") 
    * @Groups({"manage"}) 
    * 
    * @var \Acme\SearchBundle\Entity\Country 
    */ 
    private $country; 

    /** 
    * @Type("string") 
    * @Groups({"manage"}) 
    * 
    * @var string 
    */ 
    private $title; 

    /** 
    * Set description 
    * 
    * @param string $description 
    * @return Ad 
    */ 
    public function setDescription($description) 
    { 
     $this->description = $description; 

     return $this; 
    } 

    /** 
    * Get description 
    * 
    * @return string 
    */ 
    public function getDescription() 
    { 
     return $this->description; 
    } 

    /** 
    * Set country 
    * 
    * @param \Acme\SearchBundle\Entity\Country $country 
    * @return Ad 
    */ 
    public function setCountry($country) 
    { 
     $this->country= $country; 

     return $this; 
    } 

    /** 
    * Get country 
    * 
    * @return string 
    */ 
    public function getCountry() 
    { 
     return $this->country; 
    } 

    /** 
    * Set title 
    * 
    * @param string $title 
    * @return Ad 
    */ 
    public function setTituloanuncio($title) 
    { 
     $this->title = $title; 

     return $this; 
    } 

    /** 
    * Get title 
    * 
    * @return string 
    */ 
    public function getTitle() 
    { 
     return $this->title; 
    } 

} 

以及關係的實體:

class Country 
{ 

    /** 
    * @Type("string") 
    * @Groups("manage") 
    * 
    * @var string 
    */ 
    private $id; 

    /** 
    * @Type("string") 
    * @Groups("admin") 
    * 
    * @var string 
    */ 
    private $description; 

    /** 
    * Set description 
    * @Groups("") 
    * 
    * @param string $description 
    * @return Country 
    */ 
    public function setDescripcionpais($description) 
    { 
     $this->description = $description; 

     return $this; 
    } 

    /** 
    * Get description 
    * 
    * @return string 
    */ 
    public function getDescription() 
    { 
     return $this->description; 
    } 

    } 

    /** 
    * Get id 
    * 
    * @return string 
    */ 
    public function getId() 
    { 
     return $this->id; 
    } 
} 

序列化的實體,但我不知道如何將國家轉換屬性轉換爲簡單的現場。

我得到這樣的結果在JSON:

{"description":"foo", "title":"bar", "country":{"id":"en"} } 

但我希望得到國家的ID字段是這樣的:

{"description":"foo", "title":"bar", "country": "en" } 

有可能與JMS串行?

謝謝。

[編輯]

@VirtualProperty不起作用。

回答

22

是的,你可以使用@VirtualProperty註釋:

/** 
* @VirtualProperty 
* @SerializedName("foo") 
*/ 
public function bar() 
{ 
    return $this->country->getCode(); 
} 

但要知道,當涉及到反序列化:

@VirtualProperty此註釋可以在方法被定義爲 表明數據由該方法返回應該顯示爲對象的 屬性。

>注意:這僅適用於序列化,在反序列化過程中完全忽略 。

希望這有助於...

+0

謝謝!適合我。我只需要序列化。 – escrichov

+0

@VirtuaProperty不起作用。你可以給我一個例子嗎? – escrichov

+0

我很抱歉,我正在度假,沒有上網。我注意到你不接受然後接受的答案。你有沒有設法使它工作? –

8

我知道這已經回答了,但你也可以使用@Accessor。 這可能(可能,我不能確定)也適用於反序列化。

/** 
* @Type("Acme\SearchBundle\Entity\Country") 
* @Groups({"manage"}) 
* 
* @var \Acme\SearchBundle\Entity\Country 
* 
* @Serializer\Accessor(getter="getCountryMinusId",setter="setCountryWithId") 
*/ 
private $country; 

/** 
* @return string|null 
*/ 
public function getCountryMinusId() 
{ 
    if (is_array($this->country) && isset($this->country['id'])) { 
     return $this->country['id']; 
    } 

    return null; 
} 

/** 
* @param string $country 
* @return $this 
*/ 
public function setCountryWithId($country) 
{ 
    if (!is_array($this->country)) { 
     $this->country = array(); 
    ) 

    $this->country['id'] = $country; 

    return $this; 
} 
+0

Downvotes。這是錯的還是壞的? – qooplmao

+1

不錯的方法,謝謝 –

5

您可以使用@Type@Accessor註釋:

/** 
* @Type("string") 
* @Accessor(getter="serializeType",setter="setType") 
*/ 
protected $type; 
public function serializeType() 
{ 
    return $this->type->getId(); 
} 
+1

*你必須在函數中返回:'return $ this-> type-> getId();' –

16

只要遵循回答問題:

如果你不喜歡寫一個方法,你必須每個關係 - 只寫你自己的經理。這很容易像

final class RelationsHandler 
{ 
    /** 
    * @var EntityManagerInterface 
    */ 
    private $manager; 

    /** 
    * RelationsHandler constructor. 
    * 
    * @param EntityManagerInterface $manager 
    */ 
    public function __construct(EntityManagerInterface $manager) { $this->manager = $manager; } 


    public function serializeRelation(JsonSerializationVisitor $visitor, $relation, array $type, Context $context) 
    { 
     if ($relation instanceof \Traversable) { 
      $relation = iterator_to_array($relation); 
     } 

     if (is_array($relation)) { 
      return array_map([$this, 'getSingleEntityRelation'], $relation); 
     } 

     return $this->getSingleEntityRelation($relation); 
    } 

    /** 
    * @param $relation 
    * 
    * @return array|mixed 
    */ 
    protected function getSingleEntityRelation($relation) 
    { 
     $metadata = $this->manager->getClassMetadata(get_class($relation)); 

     $ids = $metadata->getIdentifierValues($relation); 
     if (!$metadata->isIdentifierComposite) { 
      $ids = array_shift($ids); 
     } 

     return $ids; 
    } 
} 

註冊的處理程序

jms_serializer.handler.relation: 
     class: MyBundle\RelationsHandler 
     arguments: 
     - "@doctrine.orm.entity_manager" 
     tags: 
     - { name: jms_serializer.handler, type: Relation, direction: serialization, format: json, method: serializeRelation} 
     - { name: jms_serializer.handler, type: Relation, direction: deserialization, format: json, method: deserializeRelation} 
     - { name: jms_serializer.handler, type: Relation<?>, direction: serialization, format: json, method: serializeRelation} 
     - { name: jms_serializer.handler, type: Relation<?>, direction: deserialization, format: json, method: deserializeRelation} 

這使您可以替換`型( 「關係」)的虛擬getter方法。

如果你還不想反序列化關係 - 你應該告訴每個@Type("Relation")它應該反序列化或包裝元數據驅動程序的類名(@Type("Relation<FQCN>"))。

public function deserializeRelation(JsonDeserializationVisitor $visitor, $relation, array $type, Context $context) 
    { 
     $className = isset($type['params'][0]['name']) ? $type['params'][0]['name'] : null; 

     if (!class_exists($className, false)) { 
      throw new \InvalidArgumentException('Class name should be explicitly set for deserialization'); 
     } 

     $metadata = $this->manager->getClassMetadata($className); 

     if (!is_array($relation)) { 
      return $this->manager->getReference($className, $relation); 
     } 

     $single = false; 
     if ($metadata->isIdentifierComposite) { 
      $single = true; 
      foreach ($metadata->getIdentifierFieldNames() as $idName) { 
       $single = $single && array_key_exists($idName, $relation); 
      } 
     } 

     if ($single) { 
      return $this->manager->getReference($className, $relation); 
     } 

     $objects = []; 
     foreach ($relation as $idSet) { 
      $objects[] = $this->manager->getReference($className, $idSet); 
     } 

     return $objects; 
    } 
+1

這感覺就像是一個更全面的答案,我會嘗試採用這種方法。謝謝! –

+0

接受的答案是正確的,但需要大量手動工作並且是隻讀的(不支持反序列化)。這個有點自動化,但是需要jms內部的未知數 – ScayTrase

+1

使用這種方法時@ScayTrase實體仍然是懶加載的。你知道這是否可以避免?使用虛擬道具接受的答案不會導致實體加載(假設您確實獲取了該ID)。 – bblue

1

作者想保留屬性名稱,它不適用於接受的答案。據我瞭解,ScayTrase的答案會保留原來的屬性名稱,但根據註釋還有另一個缺點:如果您使用Doctrine ORM @ManyToOne,則會提取相關對象,從而降低性能。

如果要保留原始屬性名稱,則必須在類別級別定義@VirtualProperty,並在原始屬性中定義@Exclude。否則,序列化屬性名稱將從getter方法派生(本例中爲countryId):

/** 
* @Serializer\VirtualProperty(
*  "country", 
*  exp="object.getCountryId()", 
*  options={@Serializer\SerializedName("country")} 
*) 
*/ 
class Ad { 
    /** 
    * @Serializer\Exclude 
    */ 
    private $country; 

    public function getCountryId() { 
     return $this->country === null ? null : $this->country->getId(); 
    } 
}