2012-07-20 9 views
15

मैं अपने ज़ेंड प्रोजेक्ट में डॉक्टर 2 ओआरएम का उपयोग कर रहा हूं और कई मामलों में जेएसओएन को अपनी इकाइयों को क्रमबद्ध करने की आवश्यकता है।JMS Serializer का उपयोग करते समय सिद्धांत 2 आलसी लोडिंग अक्षम करें?

एटीएम मैं क्वेरीबिल्डर का उपयोग करता हूं और मुझे आवश्यक सभी तालिकाओं में शामिल होता है। लेकिन मेरा सीरियलाइज़र सिद्धांत से प्रत्येक संबंधित इकाई को आलसी लोड करने का कारण बनता है जिसके परिणामस्वरूप बहुत अधिक डेटा मात्रा होती है और रिकर्सन को उत्तेजित करता है।

अब मैं डॉक्टरों को आलसी लोडिंग व्यवहार को पूरी तरह अक्षम करने का एक तरीका ढूंढ रहा हूं।

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

मैं उसमें केवल चयनित टेबल और संघों के लिए मेरी resultset चाहते हैं:

मेरे डेटा का चयन करने के लिए रास्ता निम्नलिखित होगा।

किसी भी मदद की सराहना की जाएगी।

+0

आप अपनी इकाई पोस्ट कर सकते हैं? – Stony

+0

यदि आप जेएमएस सीरिएलाइज़र का उपयोग करते हैं, तो मेरे उत्तर पर भरोसा करें। यदि आप पूरी तरह से जेएमएस सीरिएलाइज़र से बचना चाहते हैं, तो एक्सेंडर उत्तर (विशेष रूप से टिप्पणियों) पर भरोसा करें। –

उत्तर

9

सिद्धांत में जवाब देखने के बाद, मेरी टीम ने यह पता लगाया कि जेएमएस सीरियलाइज़र "समस्या" था। यह स्वचालित रूप से सिद्धांत प्रोक्सीज़ के उपयोग को ट्रिगर करता है। आलसी लोडिंग से बचने के लिए हमने जेएमएस सीरिएलाइज़र के लिए पैच लिखा था।

हम अपने DoctrineProxyHandler लागू किया जो सिर्फ तंत्र 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 सिर्फ डेटा मैं अनंत गहराई संगठनों और 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 सिर्फ होगा

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

आप कस्टम प्रॉक्सीहेडलर को कैसे सक्षम करते हैं? – vinnylinux

+0

सावधान रहें, हमने पाया कि कई और जगहें हैं जहां आलसी लोडिंग ट्रिगर होती है, हालांकि इनका उपयोग अक्सर कम होता है। अनदेखा अगर अजीब व्यवहार कर सकते हैं। –

+2

विशिष्ट रूप से जब डेटा के संग्रह लोड होते हैं (सिद्धांत \ ORM \ PersistentCollection)। –

3

डॉक्ट्रिन के क्वेरी बिल्डर का उपयोग करते समय, आप लिंक किए गए मॉडल वर्गों की आलसी लोडिंग को अक्षम नहीं कर सकते हैं। यदि आप इस तरह के व्यवहार को बाईपास करना चाहते हैं, तो आपको बेहतर सिद्धांत के साथ डेटा का अनुरोध करना होगा।

\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')); 
+0

हाय! धन्यवाद, यह पहली बार अच्छा लग रहा था - लेकिन ऐसा लगता है कि डीबीएएल क्यूबी सिर्फ एसक्यूएल स्टेटमेंट बनाने में सक्षम है जिसे मैं बाद में पीडीओ के साथ निष्पादित कर सकता हूं .. सभी ओआरएम कार्यक्षमता गायब है। –

+0

समस्या यह है कि आप जो चाहते हैं वह ओआरएम कार्यक्षमताओं को अनदेखा करना है! आप संबंधित मॉडल कक्षाओं को लोड किए बिना सिद्धांत ओआरएम के साथ एक मॉडल वर्ग लोड नहीं कर सकते हैं। – Florent

+0

शायद मैं गलत पक्ष से समस्या का सामना कर रहा हूं, और धारावाहिक को संशोधित करना शुरू कर देना चाहिए। लेकिन सीरिएलाइज़र यह नहीं जान सकता कि क्या वह कॉल जो कॉल करता है वह पहले से ही या आलसी लोड होने पर लोड होता है। मेरी इच्छा होगी कि एक संघ है कि एक में शामिल हों में उल्लेख नहीं किया गया था बस के बजाय जा रहा है आलसी लोड .. –

4

यह बहुत अच्छी तरह से एक बदसूरत बैसाखी कहा जा सकता है, लेकिन आप केवल() डेटा कि तुम सच में की जरूरत है चुन सकते हैं, तो एक सरणी क्वेरी वस्तु की विधि getArrayResult() का उपयोग करने के लिए परिणाम हाइड्रेट ..

+0

द्वारा हमने पहले इस विधि को आजमाया। लेकिन हमारे पास एक बहुत बड़ा डीबी मॉडल है और डॉक्टर मैप्ड सुपरक्लास का उपयोग करें। तो यह सरल तरीका तब तक काम करता है जब तक हम कई इकाइयों का चयन न करें जिनके बराबर नाम फ़ील्ड हैं। उदाहरण के लिए: हम सिद्धांत "संस्करण" फ़ील्ड का उपयोग कर रहे हैं, जो लगभग हर इकाई में मौजूद है। मुझे मैन्युअल रूप से "select as" entityname_version का उपयोग करना होगा - जैसा कि आपने कहा - एक बदसूरत क्रैच :( –

+1

मुझे यकीन नहीं है, लेकिन क्या यह वास्तव में सरणी की सरणी नहीं लौटाएगा, जहां प्रत्येक द्वितीय-स्तरीय सरणी एक का प्रतिनिधित्व करती है हाइड्रेटेड ऑब्जेक्ट? .. जिसका अर्थ है कि यदि आप tablename द्वारा चुनते हैं तो कोई फ़ील्ड नाम संघर्ष नहीं होगा। * – Exander

+0

संबंधित इकाइयों का डेटा एक अलग सरणी में संग्रहीत नहीं होता है, जो बदले में, अनुमोदित कुंजी के तहत संग्रहीत होता है सरणी वस्तु संघ cobntaining (हालांकि मैं द्विदिश संगठनों के बारे में भी यकीन नहीं है) का प्रतिनिधित्व करने में। – Exander

11

शामिल JMSSerializer के नवीनतम संस्करण में, जगह आप पर है

दिखना चाहिए
JMS\Serializer\EventDispatcher\Subscriber\DoctrineProxySubscriber 

Serializer\Handler\DoctrineProxyHandler 

डिफ़ॉल्ट आलसी लोड व्यवहार को ओवरराइड करने के लिए के बजाय

, एक अपने ही घटना ग्राहक परिभाषित करना चाहिए।आपका \ बंडल \ घटना \ DoctrineProxySubscriber को

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

आप JMS \ Serializer \ EventDispatcher \ सब्सक्राइबर \ DoctrineProxySubscriber से वर्ग कॉपी कर सकते हैं और $ वस्तु बाहर टिप्पणी -> __ लोड (:

अपने में app/config.yml इस ऐड); लाइन

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

धन्यवाद। यह सहायक था। हालांकि निरंतर चयन वस्तुओं को अभी भी लोड किया जा रहा है। इसे अक्षम करने के लिए कोई समाधान? – nkobber

+0

@nkobber, हाय मुझे भी एक ही समस्या का सामना करना पड़ रहा है, क्या आपको इसका समाधान मिला? – vishal

1

प्रकरण आप व्यावहारिक चाहते हैं कि आपके या डिफ़ॉल्ट ग्राहक का उपयोग करें,

@DavidLin जवाब:

आप अपने \ बंडल \ लिए JMS \ Serializer \ EventDispatcher \ सब्सक्राइबर \ DoctrineProxySubscriber से वर्ग कॉपी कर सकते हैं घटना \ DoctrineProxySubscriber और $ ऑब्जेक्ट -> __ लोड(); लाइन

<?php 

/* 
* Copyright 2013 Johannes M. Schmitt <[email protected]> 
* 
* 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'), 
     ); 
    } 
} 

और प्रारंभ की तरह serialize इस:

$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'); 
संबंधित मुद्दे