This is the best solution I came up with so far to solve the above problem. It doesn't involve changing the JMSSerializer code. Full 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 the custom DoctrineProxySubscriber, set the event type to that class. This way JMSSerializer will use that type for annotation processing so it doesn't trigger the Doctrine proxy when encountering annotations like @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 a JMSSerializer handler to add a custom handler for the empty class. This handler will just include the ID of the entity 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 }