I'm trying to serialize a entity relation with JMS Serializer.
Here is the 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;
}
}
And the Entity of the relationship:
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;
}
}
I serialize the entity but I don't know how to convert the country attribute into a simple field.
I get this result in json:
{"description":"foo", "title":"bar", "country":{"id":"en"} }
But I want to get the id field of the country like this:
{"description":"foo", "title":"bar", "country": "en" }
It is possible with JMS Serializer?
Thank you.
[EDIT]
@VirtualProperty doesn't work.
Yes, you could use @VirtualProperty
annotation:
/**
* @VirtualProperty
* @SerializedName("foo")
*/
public function bar()
{
return $this->country->getCode();
}
But be aware when it comes to deserialization:
@VirtualProperty This annotation can be defined on a method to indicate that the data returned by the method should appear like a property of the object.
> Note: This only works for serialization and is completely ignored during deserialization.
Hope this helps...
Just to follow answered question:
If you don't like writing one method for each relation you have - just write your own handler. It's easy like
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;
}
}
Register the Handler
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}
This allows you to replace virtual getter methods with `Type("Relation").
If you also want't to deserialize relation - you should tell each @Type("Relation")
the classname (@Type("Relation<FQCN>")
) which it should deserialize to or wrap the metadata driver with one which do it for you.
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;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With