16

Zend プロジェクトで Doctrine 2 ORM を使用しており、いくつかのケースでエンティティを JSON にシリアル化する必要があります。

ATM Querybuilder を使用して、必要なすべてのテーブルを結合します。しかし、私のシリアライザーは、Doctrine が関連付けられたすべてのエンティティを遅延ロードするようにします。その結果、かなり膨大な量のデータが発生し、再帰が引き起こされます。

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

選択したテーブルと関連付けのみを結果セットに含めたいと思います。

どんな助けでも大歓迎です。

4

5 に答える 5

12

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 から Your\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));
    }
}
于 2013-05-29T01:19:51.527 に答える
9

Doctrine で答えを探した後、私のチームは JMS シリアライザーが「問題」であることを突き止めました。Doctrine Proxies の使用を自動的にトリガーしました。遅延読み込みを回避するために、JMS シリアライザーのパッチを作成しました。

Doctrines の遅延読み込みメカニズムをトリガーしない独自のDoctrineProxyHandlerを実装し、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;
}

これで、テーブルを選択し、必要な関連付けに参加するだけで、無限の深さの関連付けと再帰ではなく、選択したデータだけが 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}}
  .
  .
}
于 2012-07-27T07:01:16.317 に答える
4

これは醜い松葉杖と呼ばれるかもしれませんが、本当に必要なデータを select() し、Query オブジェクトの getArrayResult() メソッドを使用して結果を配列に水和することができます...

于 2012-07-23T19:31:17.940 に答える
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'));
于 2012-07-23T09:23:21.057 に答える
1

実用的に自分またはデフォルトのサブスクライバーを使用したい場合、

@DavidLinの答え:

クラスを JMS\Serializer\EventDispatcher\Subscriber\DoctrineProxySubscriber から Your\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');
于 2016-10-10T19:44:27.857 に答える