Im using Doctrine 2 ORM in my Zend project and need to serialize my Entities to JSON in several cases.
ATM i use the Querybuilder and join all tables i need. But my serializer causes doctrine to lazy load every associated Entity which results in pretty huge data amounts and provokes recursion.
Now im looking for a way to totally disable Doctrines lazy loading behavior.
My way to select data would be the following:
$qb= $this->_em->createQueryBuilder()
->from("\Project\Entity\Personappointment", 'pa')
->select('pa', 't', 'c', 'a', 'aps', 'apt', 'p')
->leftjoin('pa.table', 't')
->leftjoin('pa.company', 'c')
->leftjoin('pa.appointment', 'a')
->leftjoin('a.appointmentstatus', 'aps')
->leftjoin('a.appointmenttype', 'apt')
->leftjoin('a.person','p')
I would like my resultset to only contain the selected tables and associations.
Any help would be greatly appreciated.
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 behavior, one should define his own event subscriber.
In your app/config.yml
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));
}
}
After having looked for the answer in Doctrine, my team figured out that the JMS Serializer was the "problem". It triggered the use of Doctrine Proxies automatically. We wrote a Patch for JMS Serializer to avoid the Lazy Loading.
We implemented our own DoctrineProxyHandler which just doesn't trigger Doctrines lazyloading mechanism and registered it within our SerializationHandlers Array.
class DoctrineProxyHandler implements SerializationHandlerInterface {
public function serialize(VisitorInterface $visitor, $data, $type, &$handled)
{
if (($data instanceof Proxy || $data instanceof ORMProxy) && (!$data->__isInitialized__ || get_class($data) === $type)) {
$handled = true;
if (!$data->__isInitialized__) {
//don't trigger doctrine lazy loading
//$data->__load();
return null;
}
$navigator = $visitor->getNavigator();
$navigator->detachObject($data);
// pass the parent class not to load the metadata for the proxy class
return $navigator->accept($data, get_parent_class($data), $visitor);
}
return null;
}
Now i can simply select my table, join the associations i need - and my JSON will contain just the data i selected instead of infinite depth associations and recursions :)
$qb= $this->_em->createQueryBuilder()
->from("\Project\Entity\Personappointment", 'pa')
->select('pa', 't', 'c', 'a')
->leftjoin('pa.table', 't')
->leftjoin('pa.company', 'c')
->leftjoin('pa.appointment', 'a')
JSON will just contain
{
Personappointment: { table {fields}, company {fields}, appointment {fields}}
Personappointment: { table {fields}, company {fields}, appointment {fields}}
Personappointment: { table {fields}, company {fields}, appointment {fields}}
.
.
}
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