我在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')
我希望我的结果集只包含选定的表和关联。
非常感谢任何帮助。
答案 0 :(得分:11)
在最新版本的JMSSerializer中,你应该看看的地方是
JMS\Serializer\EventDispatcher\Subscriber\DoctrineProxySubscriber
而不是
Serializer\Handler\DoctrineProxyHandler
要覆盖默认的延迟加载行为,应该定义自己的事件订阅者。
在app/config.yml
添加此内容:
parameters:
...
jms_serializer.doctrine_proxy_subscriber.class: Your\Bundle\Event\DoctrineProxySubscriber
您可以将类从JMS \ Serializer \ EventDispatcher \ Subscriber \ DoctrineProxySubscriber复制到您的\ Bundle \ Event \ DoctrineProxySubscriber并注释掉$ 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));
}
}
答案 1 :(得分:9)
在Doctrine中寻找答案后,我的团队发现JMS Serializer是“问题”。 它自动触发了Doctrine Proxies的使用。我们编写了一个Patch for JMS Serializer以避免延迟加载。
我们实现了自己的 DoctrineProxyHandler ,它不会触发Doctrines延迟加载机制并在我们的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}}
.
.
}
答案 2 :(得分:4)
这可能被称为丑陋的拐杖,但您可以只选择()您真正需要的数据,然后使用Query对象的getArrayResult()方法将结果水合成数组...
答案 3 :(得分:3)
使用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'));
答案 4 :(得分:1)
您希望实际使用您的或默认订阅者
的情况@DavidLin回答:
您可以将类从JMS \ Serializer \ EventDispatcher \ Subscriber \ DoctrineProxySubscriber复制到您的\ Bundle \ Event \ DoctrineProxySubscriber并注释掉$ 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');