Doctrine 2: disable lazy loading / proxy creation.

Using Doctrine 2, is it possible to:

  • Exclude property from generated proxy class?
  • Disable lazy loading / proxy creation altogether?

I am having trouble serializing my objects (using Symfony and JMS Serializer). I only want to serialize related objects that I explicitly extract into my request.

Solution described in fe Disable Doctrine 2 lazy loading when using JMS Serializer? works only partially. When you have a virtual property:

use Doctrine\ORM\Mapping as ORM;
use JMS\Serializer\Annotation as Serializer;

/**
 * Profile
 *
 * @ORM\Table(name="profile")
 * @ORM\Entity
 */
class Profile
{
    // ...

    /**
     * @return string
     *
     * @Serializer\VirtualProperty()
     */
    public function getLabel()
    {
        return implode(' ', [$this->firstname, $this->lastname]) . " ({$this->email})";
    }
}

      

the associated class is still loaded through the proxy during serialization.

+3


source to share


2 answers


This is the best solution I've come across so far to fix this issue. This is not related to a change in the JMSSerializer code. The complete code is in this Gist: https://gist.github.com/Jaap-van-Hengstum/0d400ea4f986d8f8a044

The trick is to create an empty "fake" class:

namespace MyApp\ApiBundle\Serializer;

class SerializerProxyType
{
  // this class is supposed to be empty
}

      

and in custom DoctrineProxySubscriber

set the event type for that class. So the JMSSerializer will use this type for annotation processing so that it does not call the Doctrine proxy when it encounters type annotations @VirtualProperty

.

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

        ...
        // This line is commented, so proxy loading on serializing is disabled
        // $object->__load();

        if ( ! $virtualType) {
            // This line is commented because a different type is used
            // $event->setType(get_parent_class($object));

            // This assumes that every Doctrine entity has a single 'Id' primary
            // key field.
            $event->setType('MyApp\ApiBundle\Serializer\SerializerProxyType',
                ["id" => $object->getId()]);
        }
    }

    public static function getSubscribedEvents()
    {
        return array(
            array('event' => 'serializer.pre_serialize', 'method' => 'onPreSerialize'),
        );
    }
}

      



Then you can use the JMSSerializer handler to add a custom handler for the empty class. This handler will simply include the object id in the serialized json / xml:

class DoctrineProxyHandler implements SubscribingHandlerInterface
{
    /**
     * {@inheritdoc}
     */
    public static function getSubscribingMethods()
    {
        $methods = [];

        foreach (array('json', 'xml') as $format)
        {
            $methods[] = [
                'direction' => GraphNavigator::DIRECTION_SERIALIZATION,
                'format' => $format,
                'type' => 'MyApp\\ApiBundle\\Serializer\\SerializerProxyType',
                'method' => 'serializeTo' . ucfirst($format),
            ];
        }

        return $methods;
    }

    public function serializeToJson(VisitorInterface $visitor, $entity, array $type, Context $context)
    {
        $object = new \stdClass();
        $object->id = $type['params']['id'];

        return $object;
    }

    public function serializeToXml(XmlSerializationVisitor $visitor, $entity, array $type, Context $context)
    {
        $visitor->getCurrentNode()->appendChild(
            $node = $visitor->getDocument()->createElement('id', $type['params']['id'])
        );

        return $node;
    }
}

      

To configure Symfony to use these classes:

parameters:
    jms_serializer.doctrine_proxy_subscriber.class: MyApp\ApiBundle\Serializer\DoctrineProxySubscriber

services:
  doctrineproxy_handler:
    class: MyApp\ApiBundle\Serializer\DoctrineProxyHandler
    tags:
        - { name: jms_serializer.subscribing_handler }

      

+3


source


I think this is a long bug in JMSSerializer

. Doctrine really does a pretty decent job at creating proxies / objects. In one instance, I edited the JMSSerializer source to disable loading objects from the proxy. I've always found it really annoying.

Possible workaround # 1: Set values NULL

before serialization. You will lose the proxy link for future reference, and yes, it is very ugly, but it does the job.



Possible Workaround # 2: I might be wrong, but I feel like JMSSerializer development has stalled. You can fork the project to your own GitHub, disable the lines that are fetching, and use your own fork instead.

+1


source







All Articles