2012-11-29 14 views
7

मेरे पास एक बहुत ही सरल इकाई (WpmMenu) है जो एक स्व-संदर्भ संबंध में एक दूसरे से जुड़े मेनू आइटम रखती है (जिसे विज्ञापन कहा जाता है)? तो मेरी इकाई में मेरे पास है:सिद्धांत - आत्म-संदर्भ इकाई - बच्चों की fetching अक्षम करें

protected $id 
protected $parent_id 
protected $level 
protected $name 
सभी getters/setters रिश्ते हैं साथ

:

/** 
* @ORM\OneToMany(targetEntity="WpmMenu", mappedBy="parent") 
*/ 
protected $children; 

/** 
* @ORM\ManyToOne(targetEntity="WpmMenu", inversedBy="children", fetch="LAZY") 
* @ORM\JoinColumn(name="parent_id", referencedColumnName="id", onUpdate="CASCADE", onDelete="CASCADE") 
*/ 
protected $parent; 

public function __construct() { 
    $this->children = new ArrayCollection(); 
} 

और सब कुछ ठीक काम करता है। जब मैं मेनू पेड़ प्रस्तुत करता हूं, मुझे भंडार से मूल तत्व मिलता है, अपने बच्चों को प्राप्त होता है, और उसके बाद प्रत्येक बच्चे के माध्यम से लूप मिलता है, अपने बच्चों को प्राप्त करता है और जब तक मैं प्रत्येक आइटम को प्रस्तुत नहीं करता तब तक यह पुनरावृत्तिपूर्वक करता हूं।

क्या होता है (और जो मैं समाधान की तलाश में हूं) यह है: इस समय मेरे पास 5 स्तर = 1 आइटम हैं और इनमें से प्रत्येक आइटम में 3 स्तर = 2 आइटम संलग्न हैं (और भविष्य में मैं स्तर = 3 वस्तुओं का भी उपयोग कर)। मेरे मेनू पेड़ सिद्धांत के सभी तत्वों को प्राप्त करने के लिए निष्पादित करता है: 5 बच्चों को पाने के लिए मूल तत्व के लिए

  • 1 क्वेरी +
  • 1 क्वेरी (स्तर = 1) मूल तत्व के +
  • 5 करने के लिए क्वेरी (स्तर = 2) 3 बच्चों पाने के बच्चों को पाने के लिए स्तर 1 आइटम +
  • 15 प्रश्नों (5x3) में से प्रत्येक के प्रत्येक स्तर के (स्तर = 3) 2
  • आइटम

कुल: 22 प्रश्नों

तो, मुझे इसके लिए एक समाधान खोजने की ज़रूरत है और आदर्श रूप में मैं केवल 1 क्वेरी चाहता हूं। मेरी संस्थाओं भंडार (WpmMenuRepository) मैं queryBuilder का उपयोग में और सभी मेनू आइटम स्तर द्वारा आदेश दिया की एक फ्लैट सरणी मिल:

तो यह कि मैं क्या करने की कोशिश कर रहा हूँ। मूल तत्व (WpmMenu) प्राप्त करें और तत्वों के लोड किए गए सरणी से अपने बच्चों को "मैन्युअल रूप से" जोड़ें। फिर यह बच्चों पर बार-बार करें। इस तरह से मैं एक ही पेड़ हो सकता था लेकिन एक ही प्रश्न के साथ।

तो यह है कि मैं क्या है:

WpmMenuRepository:

public function setupTree() { 
    $qb = $this->createQueryBuilder("res"); 
    /** @var Array */ 
    $res = $qb->select("res")->orderBy('res.level', 'DESC')->addOrderBy('res.name','DESC')->getQuery()->getResult(); 
    /** @var WpmMenu */ 
    $treeRoot = array_pop($res); 
    $treeRoot->setupTreeFromFlatCollection($res); 
    return($treeRoot); 
} 

और मेरे WpmMenu इकाई में मेरे पास है:

function setupTreeFromFlatCollection(Array $flattenedDoctrineCollection){ 
    //ADDING IMMEDIATE CHILDREN 
    for ($i=count($flattenedDoctrineCollection)-1 ; $i>=0; $i--) { 
    /** @var WpmMenu */ 
    $docRec = $flattenedDoctrineCollection[$i]; 
    if (($docRec->getLevel()-1) == $this->getLevel()) { 
     if ($docRec->getParentId() == $this->getId()) { 
      $docRec->setParent($this); 
      $this->addChild($docRec); 
      array_splice($flattenedDoctrineCollection, $i, 1); 
     } 
    } 
    } 
    //CALLING CHILDREN RECURSIVELY TO ADD REST 
    foreach ($this->children as &$child) { 
    if ($child->getLevel() > 0) {  
     if (count($flattenedDoctrineCollection) > 0) { 
      $flattenedDoctrineCollection = $child->setupTreeFromFlatCollection($flattenedDoctrineCollection); 
     } else { 
      break; 
     } 
    } 
    }  
    return($flattenedDoctrineCollection); 
} 

और यह क्या होता है:

सब कुछ ठीक काम करता है, लेकिन मैं दो बार मौजूद प्रत्येक मेनू आइटम के साथ समाप्त होता हूं। ;) 22 प्रश्नों के बजाय अब मेरे पास 23 है। इसलिए मैंने वास्तव में इस मामले को और खराब कर दिया।

वास्तव में क्या होता है, मुझे लगता है कि, अगर मैं बच्चों को "मैन्युअल रूप से" जोड़ता हूं, तो WpmMenu इकाई को डेटाबेस के साथ सिंक में नहीं माना जाता है और जैसे ही मैं अपने बच्चों पर फ़ोरैच लूप करता हूं ओआरएम लोडिंग में ट्रिगर किया गया है और वही बच्चों को जोड़ रहा है जो पहले से ही "मैन्युअल" जोड़े गए थे।

क्यू: क्या इस व्यवहार को अवरुद्ध/अक्षम करने का कोई तरीका है और इन इकाइयों को बताएं कि वे डीबी के साथ सिंक में हैं इसलिए कोई अतिरिक्त पूछताछ की आवश्यकता नहीं है?

+1

देखें कि यह मदद करता है: http://docs.doctrine-project.org/en/latest/reference/partial-objects.html – gremo

+0

नहीं। मेरे पास आंशिक वस्तुएं नहीं हैं और ऐसा लगता है कि इस तरह से जाना बहुत बुरा विचार है। – jakabadambalazs

उत्तर

14

अत्यधिक राहत के साथ (और सिद्धांत हाइड्रेशन और यूनिटऑफवर्क के बारे में बहुत सी सीखने के साथ) मुझे इस प्रश्न का उत्तर मिला। और जब आपको जवाब मिल जाए तो कई चीजों के साथ आपको एहसास हो जाता है कि आप कोड की कुछ पंक्तियों के साथ इसे प्राप्त कर सकते हैं। मैं अभी भी अज्ञात साइड इफेक्ट्स के लिए इसका परीक्षण कर रहा हूं लेकिन ऐसा लगता है कि यह सही तरीके से काम कर रहा है। मुझे यह पता लगाने में काफी कठिनाइयों का सामना करना पड़ा कि समस्या क्या थी - एक बार मैंने जवाब देने के लिए बहुत आसान था।

तो समस्या यह है: चूंकि यह एक आत्म-संदर्भ इकाई है जहां संपूर्ण पेड़ तत्वों की एक सपाट सरणी के रूप में लोड किया जाता है और फिर वे सेटअप द्वारा प्रत्येक तत्व के $ बच्चों की सरणी में "मैन्युअल रूप से खिलाया जाता है" TTFromFlatCollection विधि - जब पेड़ (रूट तत्व समेत) में किसी भी संस्था पर getChildren() विधि को बुलाया जाता है, सिद्धांत (इस 'मैनुअल' दृष्टिकोण के बारे में नहीं जानते) तत्व को "अंतर्निहित नहीं" के रूप में देखता है और इसलिए लाने के लिए एक एसक्यूएल निष्पादित करता है डेटाबेस से अपने सभी संबंधित बच्चे।

तो मैं ObjectHydrator वर्ग (\ सिद्धांत \ ORM \ आंतरिक \ हाइड्रेशन \ ObjectHydrator) विच्छेदित और मैं पीछा किया (एक तरह से) निर्जलीकरण प्रक्रिया और मैं एक $reflFieldValue->setInitialized(true); @line गया: 369 जो \ सिद्धांत पर एक विधि है \ ORM \ PersistentCollection क्लास कक्षा प्रारंभिक/गलत पर $ प्रारंभिक संपत्ति सेट कर रहा है। तो मैंने कोशिश की और यह काम करता है !!!

क्वेरी-बिल्डर (HYDRATE_OBJECT === ऑब्जेक्ट हाइड्रेटर का उपयोग करके) द्वारा प्राप्त प्रत्येक इकाई पर सेट - प्रारंभ (सत्य) - और फिर कॉल करना -> getChildren() इकाइयों पर अब नहीं किसी और एसक्यूएल ट्रिगर !!!

WpmMenuRepository के कोड में यह घालमेल है, यह हो जाता है:

public function setupTree() { 
    $qb = $this->createQueryBuilder("res"); 
    /** @var $res Array */ 
    $res = $qb->select("res")->orderBy('res.level', 'DESC')->addOrderBy('res.name','DESC')->getQuery()->getResult(); 
    /** @var $prop ReflectionProperty */ 
    $prop = $this->getClassMetadata()->reflFields["children"]; 
    foreach($res as &$entity) { 
    $prop->getValue($entity)->setInitialized(true);//getValue will return a \Doctrine\ORM\PersistentCollection 
    } 
    /** @var $treeRoot WpmMenu */ 
    $treeRoot = array_pop($res); 
    $treeRoot->setupTreeFromFlatCollection($res); 
    return($treeRoot); 
} 

और वह सब है!

+2

मैं दृढ़ता से कह सकता हूं कि कोई भी वेब पर कहीं भी इस विस्तृत समाधान को नहीं ढूंढ सकता है। यहां तक ​​कि सिद्धांत दस्तावेज में भी आप इस सामान्य परिस्थिति का उल्लेख नहीं कर सकते हैं जो उदाहरण के लिए बहुत कुछ होता है जब नेस्टेड मेन्यू या टिप्पणियां प्रस्तुत करते हैं। धन्यवाद @ jakabadambalazs आपने अपना दिन बनाया। – sepehr

+0

मुझे एक ही समस्या का सामना करना पड़ रहा है। समस्या यहां रेडिकेट करती है: 'प्राथमिक कुंजी द्वारा अनुक्रमित पहचान मानचित्र केवल प्राथमिक कुंजी द्वारा ऑब्जेक्ट्स मांगने पर शॉर्टकट की अनुमति देता है। इसका मतलब है कि जब आप' getChildren() 'को कॉल करते हैं तो ये सभी पृष्ठ यूनिटऑफवर्क को गैर-प्राथमिक द्वारा पूछताछ करते हैं कुंजी फ़ील्ड (उर्फ '$ पैरेंट') और फिर सिद्धांत डेटाबेस को दोबारा पूछताछ करता है ... आप जो कर रहे हैं वह मूल रूप से और अधिक आलसी लोडिंग को रोक रहा है और बच्चों को पर्सिस्टेंट कोलेक्शन को मैन्युअल रूप से प्रारंभ कर रहा है ... क्योंकि आप केवल बच्चों पर '$ प्रारंभिक' सेट कर रहे हैं 'PersistentCollection' मुझे कोई कमी नहीं दिखाई दे रही है .. – KnF

+0

यह वास्तव में एक हैकी समाधान है .. लेकिन – KnF

0

उत्सुक लोडिंग सक्षम करने के लिए अपनी एसोसिएशन को एनोटेशन जोड़ें। इससे आपको पूरे पेड़ को केवल 1 क्वेरी के साथ लोड करने की अनुमति मिलनी चाहिए, और इसे एक फ्लैट सरणी से पुनर्निर्मित करने से बचें।

उदाहरण:

/** 
* @ManyToMany(targetEntity="User", mappedBy="groups", fetch="EAGER") 
*/ 

एनोटेशन इस एक है, लेकिन मूल्य के साथ बदल https://doctrine-orm.readthedocs.org/en/latest/tutorials/extra-lazy-associations.html?highlight=fetch

+1

कोई सिगार नहीं! जैसे ही मैं पेड़ के मूल तत्व को प्राप्त करता हूं, रिश्ते पर 'fetch =" EAGER "विकल्प पूरे पेड़ की लोडिंग को सही ढंग से लोड करता है। हालांकि ऐसा करने के लिए सभी अन्य प्रश्नों को 'WHERE t0.parent_id =?' के साथ पहले से निष्पादित किया जाता है। दूसरे शब्दों में, केवल परिवर्तन यह है कि प्रश्नों को निष्पादित किया जाता है भले ही मैं बच्चों तक नहीं पहुंच पाता लेकिन वास्तविक संरचना और इसलिए तत्वों को लाने के लिए आवश्यक प्रश्न समान हैं - इसलिए परिणाम वही है – jakabadambalazs

0

यदि आसन्न सूची का उपयोग कर आप इस समस्या को हल नहीं कर सकते। वहाँ किया गया था कि। एकमात्र तरीका नेस्टेड-सेट का उपयोग करना है और फिर आप एक ही क्वेरी में जो कुछ भी चाहते हैं उसे प्राप्त करने में सक्षम होंगे।

मैंने ऐसा किया जब मैं Doctrine1 का उपयोग कर रहा था। नेस्टेड-सेट में आपके पास root, level, left और right कॉलम हैं जिनका उपयोग आप प्राप्त वस्तुओं को सीमित/विस्तारित करने के लिए कर सकते हैं। इसमें कुछ जटिल सबक्वायरी की आवश्यकता होती है लेकिन यह करने योग्य है।

नेस्टेड सेट के लिए डी 1 दस्तावेज बहुत अच्छा है, मैं इसे जांचने का सुझाव देता हूं और आप इस विचार को बेहतर समझेंगे।

+0

धन्यवाद, लेकिन फिर भी अंधेरा! यही वह है जो मुझे मिला: 1) नेस्टेड सेट वास्तव में अच्छे हैं - मैंने इसे पढ़ने के लिए 4 घंटे बिताए और मैंने एक बहुत अच्छा सौदा सीखा लेकिन दिन के अंत में मैं QueryBuilder के साथ एक प्रश्न कर रहा हूं जिस पर मैं क्वेरीब्यूल्डर के साथ एक प्रश्न करूँगा कर सकते हैं: '-> getResult()'/'-> getArrayResult()'/'-> getScalarResult()' और इन विधियों में से कोई भी मुझे जो चाहिए वह वापस कर देगा। '-> getArrayResult()' निकटतम है 'क्योंकि यह सरणी का घोंसला वाला सरणी देता है (कम से कम यह ऐसा लगता है जो ऐसा लगता है लेकिन मुझे अभी भी इसकी जांच करने की आवश्यकता है) - लेकिन मुझे लगता है कि वास्तविक समस्या नहीं है मैं डीबी में अपना डेटा कैसे डालता हूं लेकिन हाइड्रेशन – jakabadambalazs

+0

वास्तव में, जैसा कि यह है (https://doctrine-orm.readthedocs.org/en/latest/reference/dql-doctrine-query-language.html? हाइलाइट = हाइड्रेशन # हाइड्रेशन-मोड) सिद्धांत संदर्भ पुस्तक @ 14.7.4 - हाइड्रेशन मोड - ऐसा कुछ भी नहीं लगता है जो एकल परिणाम सेट पर पेड़ की तरह संरचना के लिए फ्लैट 2 डी डेटा हाइड्रेट करेगा। कोई फर्क नहीं पड़ता कि आसन्न सूची या नेस्टेड सेट। – jakabadambalazs

+0

मुझे जांचने दें कि क्या मैंने आपको सही ढंग से समझा है; आप एक प्रश्न पूछना चाहते हैं जो पूरे पेड़ को लाएगा, शायद कुछ जुड़ने के साथ भी, सही? यदि ऐसा है, तो आपको वास्तव में नेस्टेड-सेट का उपयोग करना होगा। मैं अपने पास नमूना कोड कॉपी और पेस्ट कर सकता हूं लेकिन यह डी 1 के लिए है और प्रोजेक्ट के लिए बहुत विशिष्ट है, इसलिए यह आपके लिए अधिक उपयोग नहीं करेगा। यह चाल 'बाएं' और 'दाएं' कॉलम के चालाक उपयोग में थी। सबसे आसान समाधान सिर्फ एक और इकाई बनाना है जो नेस्टेड सेट है, कुछ पेड़ बनाएं और डीबी में मूल्यों की जांच करें। फिर आपको यह पता चल जाएगा कि यह कैसे करें। और बीटीडब्ल्यू; मैं वस्तुओं के लिए हाइड्रेटेड, कोई रूपांतरण की आवश्यकता नहीं थी। – Zeljko

0

यह एक पूर्णता और अधिक क्लीनर समाधान की तरह है, लेकिन स्वीकार्य उत्तर पर आधारित है ...

एकमात्र चीज की आवश्यकता है एक कस्टम रिपोजिटरी जो फ्लैट पेड़ संरचना से पूछताछ करने जा रही है, और फिर, इस सरणी को पुन: स्थापित करके, पहले बच्चों के संग्रह को प्रारंभिक रूप से चिह्नित किया जाएगा और फिर इसे एडचल्ड सेटटर के साथ हाइड्रेटेट करेगा मूल इकाई ..

<?php 

namespace Domain\Repositories; 

use Doctrine\ORM\EntityRepository; 

class PageRepository extends EntityRepository 
{ 
    public function getPageHierachyBySiteId($siteId) 
    { 
     $roots = []; 
     $flatStructure = $this->_em->createQuery('SELECT p FROM Domain\Page p WHERE p.site = :id ORDER BY p.order')->setParameter('id', $siteId)->getResult(); 

     $prop = $this->getClassMetadata()->reflFields['children']; 
     foreach($flatStructure as &$entity) { 
      $prop->getValue($entity)->setInitialized(true); //getValue will return a \Doctrine\ORM\PersistentCollection 

      if ($entity->getParent() != null) { 
       $entity->getParent()->addChild($entity); 
      } else { 
       $roots[] = $entity; 
      } 
     } 

     return $roots; 
    } 
} 

संपादित करें: getParent() विधि जब तक संबंध प्राथमिक कुंजी के लिए किया जाता अतिरिक्त प्रश्नों शुरू नहीं करता, मेरे मामले में, $ माता पिता विशेषता के लिए एक सीधा संबंध है पीके, तो यूनिटऑफवर्क कैश्ड इकाई वापस कर देगा और डेटाबेस से पूछताछ नहीं करेगा .. यदि आपकी संपत्ति पीके से संबंधित नहीं है, तो यह अतिरिक्त प्रश्न उत्पन्न करेगी।

संबंधित मुद्दे