2013-04-13 7 views
25

Ich versuche eine Entitätsbeziehung mit JMS Serializer zu serialisieren. HierSerialisierung der Entitätsbeziehung nur zur ID mit JMS Serializer

ist die Entity:

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

} 

und das Unternehmen von der Beziehung:

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

ich an das Unternehmen zu serialisiert, aber ich weiß nicht, wie das Land zu konvertieren Attribut in ein einfaches Feld .

ich dieses Ergebnis in json:

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

Aber ich möchte das ID-Feld des Landes wie folgt erhalten:

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

Es ist möglich, mit JMS Serializer?

Vielen Dank.

[EDIT]

@VirtualProperty funktioniert nicht.

Antwort

22

Ja, könnten Sie @VirtualProperty Anmerkung verwenden:

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

aber bewusst sein, wenn es um die Deserialisierung kommt:

@VirtualProperty Diese Anmerkung auf ein Verfahren zur zeigen definiert werden kann, dass die Daten zurückgegeben von der Methode sollte wie eine Eigenschaft des Objekts angezeigt werden.

> Hinweis: Dies funktioniert nur für die Serialisierung und wird während der Deserialisierung vollständig ignoriert .

Hope this helps ...

+0

Thank you! Perfekt für mich. Ich musste nur serialisieren. – escrichov

+0

@VirtuaProperty funktioniert nicht. Kannst du mir ein Beispiel geben? – escrichov

+0

Ich entschuldige mich, ich war im Urlaub und hatte keinen Zugang zum Internet. Ich habe bemerkt, dass du die Antwort nicht angenommen hast. Hast du es geschafft, es zum Laufen zu bringen? –

8

Ich weiß, das bereits beantwortet worden, aber man könnte auch @Accessor verwenden. Dies funktioniert wahrscheinlich (kann ich nicht sicher sein) auch mit Deserialisierung.

/** 
* @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. Ist das falsch oder nur schlecht? – qooplmao

+1

nice way auch, danke –

5

können Sie verwenden @Type und @Accessor Anmerkungen:

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

* Sie müssen in der Funktion zurückkehren: 'return $ this-> type-> getId();' –

16

Nur beantworteten Frage zu folgen:

Wenn Sie nicht wie eine Methode für jede Beziehung Schreiben Sie haben - nur Ihre schreiben eigener Handler. Es ist einfach, wie

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} 

Dies ermöglicht es dem Handler registrieren Sie virtuelle Getter-Methoden mit `Type ("Relation") zu ersetzen.

Wenn Sie auch keine Relation deserialisieren möchten, sollten Sie jedem @Type("Relation") den Klassennamen (@Type("Relation<FQCN>")), zu dem er deserialisieren sollte, mitteilen oder den Metadatentreiber mit einem Code umhüllen, der dies für Sie erledigt.

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

Das fühlt sich an wie eine viel besser abgerundete Antwort, ich werde versuchen, diesen Ansatz zu übernehmen. Vielen Dank! –

+0

Akzeptierte Antwort ist korrekt, erfordert jedoch viel manuelle Arbeit und ist schreibgeschützt (unterstützt keine Deserialisierung). Dieser ist ein wenig automatisiert, benötigt aber keine JMS-Interna – ScayTrase

+1

@ScayTrase Entities sind immer noch faul geladen, wenn Sie diesen Ansatz verwenden. Weißt du, ob das vermieden werden kann? Die Verwendung der akzeptierten Antwort mit virtuellen Requisiten führt nicht dazu, dass die Entität geladen wird (vorausgesetzt, dass Sie tatsächlich die ID abrufen). – bblue

1

Der Autor möchte den Namen der Eigenschaft behalten, die nicht auf die angenommene Antwort zutrifft. Soweit ich verstanden habe, behielte die Antwort von ScayTrase den ursprünglichen Eigenschaftsnamen, hat aber nach den Kommentaren einen weiteren Nachteil: Das verwandte Objekt wird abgerufen, wenn Sie Doctrine ORM @ManyToOne verwenden, wodurch die Leistung sinkt. Wenn Sie den ursprünglichen Eigenschaftsnamen beibehalten möchten, müssen Sie @VirtualProperty auf Klassenebene und @Exclude die ursprüngliche Eigenschaft definieren. Andernfalls werden die serialisiert Eigenschaftsnamen aus der Getter-Methode (countryId in diesem Fall) abgeleitet werden:

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