Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Avoiding recursion with Doctrine entities and JMSserializer

I am building a REST API using Symfony2, Doctrine, FOSRestBundle and JMSSerializer.

The issue I am having is when serializing my entities, the serializer pulls in any related entities. Eg for a task that is part of a story which is part of a board, so when serializing the task I get output that includes the story which includes the board, which then includes all other stories on the board.

Is there an easy way to limit this, and just include the foreignIds instead?

like image 760
WayneC Avatar asked Aug 07 '12 17:08

WayneC


2 Answers

Use JMS exclusion policy.

Example using annotations on category entity, where you don't want to include children and product related entities to be included:

use ...
    JMS\SerializerBundle\Annotation\ExclusionPolicy,
    JMS\SerializerBundle\Annotation\Exclude,
    ...;

/**
 * ...
 * @ExclusionPolicy("none")
 */
class Category
{
   /**
    * ...
    * @Exclude
    */
   private $children;

   /**
    * ...
    * @Exclude
    */
   private $products;

}

Look at the JMSSerializer docs for more information.

EDIT:

For example you could use partial keyword to select only data that you need. Although I could not, for the life of me, disable the loading of the full related entities (two levels down) if I pass entity object to the serializer (even when disabling load in DoctrineProxyHandler), but if I use an array, than it doesn't use doctrine lazy loading though proxies (as expected ofc).

Example using your example entities:

$dql = "SELECT t, s, partial b.{id}, partial ss.{id}
        FROM Acme\AppBundle\Entity\Task t
        JOIN t.story s
        JOIN s.board b
        JOIN b.stories ss"

$q = $this->_em-createQuery($dql);

$result = $q->getArrayResult();

This way you would get something like:

[
{
    id: 33,
    title: "My Task",
    story: [
    {
        id: 554,
        board: [
        {
            id: 14,
            stories: [
            {
                id: 554
            },
            {
                id: 3424
            },
            {
                id: 3487
            }
            ]
        }
        ]
    }
    ]

}
]

P.S. I'm actually intrigued by this "problem". Anyway I'll see to come up with solution to how to serialize entity object without using array result.

like image 106
Marko Jovanović Avatar answered Sep 28 '22 09:09

Marko Jovanović


Just an update in the latest version of JMSSerializer, the place you should look at is

JMS\Serializer\EventDispatcher\Subscriber\DoctrineProxySubscriber

instead of

Serializer\Handler\DoctrineProxyHandler

To override the default lazy load behaviour, one should define his own event subscriber.

In your app/config.yuml add this:

parameters:
    ...
    jms_serializer.doctrine_proxy_subscriber.class: Your\Bundle\Event\DoctrineProxySubscriber

you can copy the class from JMS\Serializer\EventDispatcher\Subscriber\DoctrineProxySubscriber to Your\Bundle\Event\DoctrineProxySubscriber and comment out the $object->__load(); line

public function onPreSerialize(PreSerializeEvent $event)
{
    $object = $event->getObject();
    $type = $event->getType();

    // If the set type name is not an actual class, but a faked type for which a custom handler exists, we do not
    // modify it with this subscriber. Also, we forgo autoloading here as an instance of this type is already created,
    // so it must be loaded if its a real class.
    $virtualType = ! class_exists($type['name'], false);

    if ($object instanceof PersistentCollection) {
        if ( ! $virtualType) {
            $event->setType('ArrayCollection');
        }

        return;
    }

    if ( ! $object instanceof Proxy && ! $object instanceof ORMProxy) {
        return;
    }

     //$object->__load(); Just comment this out

    if ( ! $virtualType) {
        $event->setType(get_parent_class($object));
    }
}

Update: I ended up writing my own simplified version of serialisation tool: https://github.com/dlin-me/array-converter-bundle

like image 25
David Lin Avatar answered Sep 28 '22 10:09

David Lin