使用JMS序列化程序时禁用Doctrine 2延迟加载


Disable Doctrine 2 lazy loading when using JMS Serializer?

我在Zend项目中使用Doctrine 2 ORM,在某些情况下需要将我的实体序列化为JSON。

ATM我使用Querybuilder并连接我需要的所有表。但我的序列化程序导致条令延迟加载每个相关的实体,这会导致相当大的数据量并引发递归。

现在我正在寻找一种完全禁用Doctrines懒惰加载行为的方法。

我选择数据的方法如下:

$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')

我希望我的结果集只包含选定的表和关联。

如有任何帮助,我们将不胜感激。

在最新版本的JMSSerializer中,您应该查看

JMS'Serializer'EventDispatcher'Subscriber'DoctrineProxySubscriber

而不是

Serializer'Handler'DoctrineProxyHandler

要覆盖默认的延迟加载行为,应该定义自己的事件订阅者。

在您的app/config.yml中添加以下内容:

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

您可以将类从JMS''Serizer''EventDispatcher''Subscriber''DoctrineProxy Subscriber复制到您的''Bundle''Event''DoctrineProxy Subscriber,并注释掉$object->__load();线路

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));
    }
}

在Doctrine中寻找答案后,我的团队发现JMS序列化程序是"问题"。它自动触发了条令代理的使用。我们为JMS序列化程序编写了一个补丁,以避免延迟加载。

我们实现了自己的DoctrineProxy处理程序,它只是不触发Doctrines lazyloading机制,并在SerializationHandlers数组中注册了它。

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;
}

现在我可以简单地选择我的表,加入我需要的关联——我的JSON将只包含我选择的数据,而不是无限深度的关联和递归:)

$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将只包含

{  
  Personappointment: { table {fields}, company {fields}, appointment {fields}}
  Personappointment: { table {fields}, company {fields}, appointment {fields}}
  Personappointment: { table {fields}, company {fields}, appointment {fields}}
  .
  .
}

这很可能被称为丑陋的拐杖,但您可以只选择()真正需要的数据,然后使用Query对象的getArrayResult()方法将结果合并到一个数组中。。。

使用Doctrine的查询生成器时,不能禁用链接模型类的延迟加载。如果你想绕过这种行为,你最好向Doctrine的DBAL请求数据。

不要使用'Doctrine'ORM'QueryBuilder,而是使用'Doctrine'DBAL'Query'QueryBuilder

$qb = new QueryBuilder($this->_em->getConnection());
$expr = $qb->expr();
$qb->select('pa.*', 't.*', 'c.*', 'a.*', 'aps.*', 'apt.*', 'p.*')
   ->from('person_appointment', 'pa')
   ->leftJoin('pa', 'table', 't', $expr->eq('pa.table_id', 't.table_id'))
   // put other joints here
   // ...
   ->leftjoin('a', 'person', 'p', $expr->eq('a.person_id', 'p.person_id'));

如果您希望实际使用您的或默认订阅者

@林回答:

您可以将类从JMS''Serizer''EventDispatcher''Subscriber''DoctrineProxy Subscriber复制到您的''Bundle''Event''DoctrineProxy Subscriber,并注释掉$object->__load();线路

<?php
/*
 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
namespace Your'Bundle'Event;
use Doctrine'ORM'PersistentCollection;
use Doctrine'ODM'MongoDB'PersistentCollection as MongoDBPersistentCollection;
use Doctrine'ODM'PHPCR'PersistentCollection as PHPCRPersistentCollection;
use Doctrine'Common'Persistence'Proxy;
use Doctrine'ORM'Proxy'Proxy as ORMProxy;
use JMS'Serializer'EventDispatcher'PreSerializeEvent;
use JMS'Serializer'EventDispatcher'EventSubscriberInterface;
class AvoidDoctrineProxySubscriber implements EventSubscriberInterface
{
    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
            || $object instanceof MongoDBPersistentCollection
            || $object instanceof PHPCRPersistentCollection
        ) {
            if ( ! $virtualType) {
                $event->setType('ArrayCollection');
            }
            return;
        }
        if ( ! $object instanceof Proxy && ! $object instanceof ORMProxy) {
            return;
        }

        //Avoiding doctrine lazy load proxyes
        //$object->__load();
        if ( ! $virtualType) {
            $event->setType(get_parent_class($object));
        }
    }
    public static function getSubscribedEvents()
    {
        return array(
            array('event' => 'serializer.pre_serialize', 'method' => 'onPreSerialize'),
        );
    }
}

并像这样初始化序列化

$serializer = JMS'Serializer'SerializerBuilder::create()
    //remove this to use lazy loading 
    ->configureListeners(function(JMS'Serializer'EventDispatcher'EventDispatcher $dispatcher) {
        $dispatcher->addSubscriber(new Your'Bundle'Event'AvoidDoctrineProxySubscriber());
    })  
    // !remove this to use lazy loading 
    ->build();
//and serialize the data with/without Lazy
serializer->serialize($data, 'json');