Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JMSSerializerBundle serialization groups in entities with relations

I have a problem with serializing entity with many relations using groups. I have a problem with serializing related entities this way.

Let's say I have two entities: Product and related Element.

/**
 *
 * @Serializer\ExclusionPolicy("none")
 */
class Product {

    /**
     * Primary key
     * @var integer $id
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     * 
     * @Serializer\Groups({"list","details"})
     * @Serializer\Type("integer")
     */
    protected $id;

    /**
     * @Serializer\Groups({"list","details"})
     * @Serializer\Type("string")
     */
    protected $name;

    /**
     * @ORM\Column(name="description", type="string", length=4096, nullable=true)
     * 
     * @Serializer\Groups({"details"})
     * @Serializer\Type("string")
     */
    protected $description;

    /**
     * @var ArrayCollection
     * 
     * @ORM\OneToMany(targetEntity="Madden\ProjectBundle\Entity\ProjectResource", mappedBy="project")
     * @Serializer\Groups({"details"})
     * @Serializer\Type("ArrayCollection<Element>")
     */
    protected $details1;

    /**
     * Relation to project tasks
     * @ORM\OneToMany(targetEntity="Madden\ProjectBundle\Entity\ProjectTask", mappedBy="project")
     * @Serializer\Exclude()
     * @Serializer\Type("ArrayCollection<Element>")
     */
    protected $details2;

    ...

}

Element entity has a similar structure:

/**
 *
 * @Serializer\ExclusionPolicy("none")
 */
class Element {

    /**
     * Primary key
     * @var integer $id
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     * 
     * @Serializer\Groups({"list","details"})
     * @Serializer\Type("integer")
     */
    protected $id;

    /**
     * @Serializer\Groups({"list","details"})
     * @Serializer\Type("string")
     */
    protected $name;

    /**
     * @ORM\Column(name="description", type="string", length=4096, nullable=true)
     * 
     * @Serializer\Groups({"details"})
     * @Serializer\Type("string")
     */
    protected $description;

    ...
}

My problem is that when I'm serializing Product with 'details' group entity I want to serialize only id's of Elements but as you see entity has defined same groups as Product (in case that I would need details of element object) because I want have unified groups on all my entities and prevent making hundreds of groups like 'product_details','element_details', and so on.

Is there a way to eventualy change serialization group when I visit relation or something like that? Handler maybe or something like that?

Regards and thanks for any help

like image 871
mrMantir Avatar asked Oct 05 '12 12:10

mrMantir


People also ask

How do I use jmsserializerbundle?

JMSSerializerBundle allows you to serialize your data into a requested output format such as JSON, XML, or YAML, and vice versa. You can learn more in the documentation for the standalone library. You can install this bundle using composer or add the package to your composer.json file directly.

When do we need to implement serialization in Entity Framework?

If an entity instance is to be passed by value as a detached object (e.g., through a remote interface), the entity class must implement the Serializable interface. In practice, if our object is to leave the domain of the JVM, it'll require serialization. Each entity class consists of persistent fields and properties.

How do I use YAML or XML in the serializer configuration?

If you want to use YAML or XML, please add the mapping path in the serializer configuration: Add the normalization context and denormalization context attributes to the resource, and specify which groups to use. Here you see that we add read and write, respectively. You can use any group names you wish.

How does the serializer work in API platform?

By default, the serializer provided with API Platform represents relations between objects using dereferenceable IRIs . They allow you to retrieve details for related objects by issuing extra HTTP requests. However, for performance reasons, it is sometimes preferable to avoid forcing the client to issue extra HTTP requests.


1 Answers

Unfortunately, you can't really (but keep reading ;-)), well at least not without changes to the serializer library. The culprit is that the list of groups is fixed within a GroupExclusionStrategy (which is referenced by the Context) the minute you start the serialization process. There is actually an assertion within the code that prevents modification of the exclusion strategy once the (de-)serialization is running.

But as it happens, I had the exact same problem in a project of mine as well, and I hacked the necessary changes into the serializer code. I have cleaned the code up a bit and uploaded it to Github (https://github.com/andreasferber/serializer/tree/recursion-groups).

It adds new property metadata with which you can add, remove or override the groups when descending into subobjects. With annotations it looks like this:

/**
 * @Serializer\RecursionGroups(set={"foo", "bar"}, add={"baz"}, remove={"Default"})
 */
private $myProperty;

You should be able to use XML or Yaml metadata as well, however this is untested since I don't use them and I haven't added test cases yet. Have a look at the reference documentation. Since I haven't done any optimizations yet either, if your entities are really large and deeply nested, it might have a noticable performance impact.

Please let me know if you find this useful, or if you have any suggestions, because if this isn't only needed by me, I will add some tests and try to submit it upstream.

like image 83
aferber Avatar answered Oct 06 '22 21:10

aferber