2014-05-09 8 views
5

के साथ कई से कई संगठनों पर फ़िल्टरिंग मेरे पास Account इकाई है जिसमें Section इकाइयों का संग्रह है। प्रत्येक Section इकाई का संग्रह Element इकाइयों (OneToMany एसोसिएशन) का संग्रह है। मेरी समस्या यह है कि किसी सेक्शन के सभी तत्वों को लाने के बजाय, मैं एक सेक्शन और से संबंधित सभी तत्वों को एक विशिष्ट खाते से जोड़ना चाहता हूं। नीचे मेरा डेटाबेस मॉडल है।Doctrine2

Database model

इस प्रकार, जब मैं कोई खाता लाने, मैं (इस हिस्से कोई समस्या नहीं है) उसके संबंधित वर्गों के माध्यम से लूप करने में सक्षम होना चाहते हैं, और प्रत्येक अनुभाग के लिए, मैं उसके तत्वों हैं कि के माध्यम से लूप करना चाहते हैं प्राप्त खाते से जुड़े। अभी मेरे पास निम्न कोड है।

$repository = $this->objectManager->getRepository('MyModule\Entity\Account'); 
$account = $repository->find(1); 

foreach ($account->getSections() as $section) { 
    foreach ($section->getElements() as $element) { 
     echo $element->getName() . PHP_EOL; 
    } 
} 

समस्या यह है कि यह किसी दिए गए खंड से संबंधित सभी तत्वों को प्राप्त करता है, भले ही वे किस खाते से जुड़े हों। एक सेक्शन के तत्व लाने के लिए उत्पन्न एसक्यूएल निम्नानुसार है।

SELECT t0.id AS id1, t0.name AS name2, t0.section_id AS section_id3 
FROM mydb.element t0 
WHERE t0.section_id = ? 

मुझे ऐसा करने की ज़रूरत है जो नीचे की तरह कुछ है (कोई अन्य दृष्टिकोण हो सकता है)। यह महत्वपूर्ण है कि फ़िल्टरिंग एसक्यूएल के साथ की जाती है।

SELECT e.id, e.name, e.section_id 
FROM element AS e 
INNER JOIN account_element AS ae ON (ae.element_id = e.id) 
WHERE ae.account_id = ? 
AND e.section_id = ? 

मुझे पता है कि मैं एक विधि getElementsBySection($accountId) या एक कस्टम भंडार में समान लिखने और DQL का उपयोग कर सकते है। यदि मैं ऐसा कर सकता हूं और Section इकाई पर getElements() विधि को किसी भी तरह ओवरराइड कर सकता हूं, तो यह सही होगा। अगर मैं एसोसिएशन मैपिंग्स के माध्यम से या कम से कम मौजूदा गेटर विधियों का उपयोग करके ऐसा करने का कोई तरीका होगा तो मैं बहुत पसंद करूंगा। आदर्श रूप से, खाता ऑब्जेक्ट का उपयोग करते समय, मैं उपरोक्त कोड स्निपेट की तरह लूप करने में सक्षम होना चाहता हूं ताकि ऑब्जेक्ट का उपयोग करते समय "खाता बाधा" को सारणीबद्ध किया जा सके। यही है, ऑब्जेक्ट के उपयोगकर्ता को getElementsByAccount() या Section ऑब्जेक्ट पर कॉल करने की आवश्यकता नहीं है, क्योंकि यह कम सहज लगता है।

मैंने Criteria ऑब्जेक्ट में देखा, लेकिन जहां तक ​​मुझे याद है, इसका उपयोग एसोसिएशन पर फ़िल्टर करने के लिए नहीं किया जा सकता है।

तो, इसे पूरा करने का सबसे अच्छा तरीका क्या है? क्या यह Section इकाई को डीक्यूएल प्रश्नों के उपयोग के माध्यम से तत्वों के साथ "मैन्युअल रूप से" इकट्ठा किए बिना संभव है? मेरा वर्तमान (और छोटा) स्रोत कोड नीचे देखा जा सकता है। आपका अग्रिम रूप से बोहोत धन्यवाद!

/** 
* @ORM\Entity 
*/ 
class Account 
{ 
    /** 
    * @var int 
    * @ORM\Column(type="integer") 
    * @ORM\Id 
    * @ORM\GeneratedValue 
    */ 
    protected $id; 

    /** 
    * @var string 
    * @ORM\Column(type="string", length=50, nullable=false) 
    */ 
    protected $name; 

    /** 
    * @var ArrayCollection 
    * @ORM\ManyToMany(targetEntity="MyModule\Entity\Section") 
    * @ORM\JoinTable(name="account_section", 
    *  joinColumns={@ORM\JoinColumn(name="account_id", referencedColumnName="id")}, 
    *  inverseJoinColumns={@ORM\JoinColumn(name="section_id", referencedColumnName="id")} 
    *) 
    */ 
    protected $sections; 

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

    // Getters and setters 
} 


/** 
* @ORM\Entity 
*/ 
class Section 
{ 
    /** 
    * @var int 
    * @ORM\Id 
    * @ORM\GeneratedValue 
    * @ORM\Column(type="integer") 
    */ 
    protected $id; 

    /** 
    * @var string 
    * @ORM\Column(type="string", length=50, nullable=false) 
    */ 
    protected $name; 

    /** 
    * @var ArrayCollection 
    * @ORM\OneToMany(targetEntity="MyModule\Entity\Element", mappedBy="section") 
    */ 
    protected $elements; 

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

    // Getters and setters 
} 


/** 
* @ORM\Entity 
*/ 
class Element 
{ 
    /** 
    * @var int 
    * @ORM\Id 
    * @ORM\GeneratedValue 
    * @ORM\Column(type="integer") 
    */ 
    protected $id; 

    /** 
    * @var string 
    * @ORM\Column(type="string", length=50, nullable=false) 
    */ 
    protected $name; 

    /** 
    * @var Section 
    * @ORM\ManyToOne(targetEntity="MyModule\Entity\Section", inversedBy="elements") 
    * @ORM\JoinColumn(name="section_id", referencedColumnName="id") 
    */ 
    protected $section; 

    /** 
    * @var \MyModule\Entity\Account 
    * @ORM\ManyToMany(targetEntity="MyModule\Entity\Account") 
    * @ORM\JoinTable(name="account_element", 
    *  joinColumns={@ORM\JoinColumn(name="element_id", referencedColumnName="id")}, 
    *  inverseJoinColumns={@ORM\JoinColumn(name="account_id", referencedColumnName="id")} 
    *) 
    */ 
    protected $account; 

    // Getters and setters 
} 

उत्तर

1

अगर मैं सही ढंग से समझ, आप एक Account के सभी Section रों के सभी Element रों पुनः प्राप्त करने में सक्षम होना चाहते हैं, लेकिन उन Element कि Account, और खाते में एक गेटर से इस के साथ जुड़े रहे करते हों।

सबसे पहले: एक इकाई को भंडारों के बारे में कभी नहीं पता होना चाहिए। यह एक डिजाइन सिद्धांत को तोड़ता है जो आपको दृढ़ता परत को स्वैप करने में मदद करता है। यही कारण है कि आप एक इकाई के भीतर से एक भंडार का उपयोग आसान नहीं कर सकते हैं।

class Section 
{ 
    /** 
    * @param Account $accout 
    * @return Element[] 
    */ 
    public function getElementsByAccount(Account $accout) 
    { 
     $elements = array(); 

     foreach ($this->getElements() as $element) { 
      if ($element->getAccount() === $account) { 
       $elements[] = $element->getAccount(); 
      } 
     } 

     return $elements; 
    } 
} 

class Account 
{ 
    /** 
    * @return Element[] 
    */ 
    public function getMyElements() 
    { 
     $elements = array() 

     foreach ($this->getSections() as $section) { 
      foreach ($section->getElementsByAccount($this) as $element) { 
       $elements[] = $element; 
      } 
     } 

     return $elements; 
    } 
} 

भंडार

समाधान:

Getters केवल

आप केवल सत्ता के बारे में ही टिककर खेल उपयोग करना चाहते हैं, तो आप इस जोड़कर 2 तरीकों का पालन करने के लिए हल कर सकते हैं उपरोक्त कई प्रश्न पूछने की संभावना है, कितने Section एस और Element एस के आधार पर सटीक राशि Account पर ध्यान दिया गया।

जब आप रिपोजिटरी विधि का उपयोग करते हैं तो आपको प्रदर्शन में वृद्धि होने की संभावना है, ताकि आप जो भी चाहते हैं उसे पुनर्प्राप्त करने के लिए उपयोग की गई क्वेरी/क्वेरी को अनुकूलित कर सकें।

एक उदाहरण:

class ElementRepository extends EntityRepository 
{ 
    /** 
    * @param Account $account [description] 
    * @return Element[] 
    */ 
    public function findElementsByAccount(Account $account) 
    { 
     $dql = <<< 'EOQ' 
SELECT e FROM Element e 
JOIN e.section s 
JOIN s.accounts a 
WHERE e.account = ?1 AND a.id = ?2 
EOQ; 

     $q = $this->getEntityManager()->createQuery($dql); 
     $q->setParameters(array(
      1 => $account->getId(), 
      2 => $account->getId() 
     )); 

     return $q->getResult(); 
    } 
} 

पुनश्च: इस क्वेरी काम करने के लिए, आप एक द्विदिश एक के रूप में Section और Account के बीच ManyToMany संघ को परिभाषित करने की आवश्यकता होगी।

प्रॉक्सी विधि

एक संकर समाधान Account के लिए एक प्रॉक्सी विधि जोड़ने के लिए, भंडार आप इसे पारित करने के लिए कॉल अग्रेषित करता है कि होगा।

class Account 
{ 
    /** 
    * @param ElementRepository $repository 
    * @return Element[] 
    */ 
    public function getMyElements(ElementRepository $repository) 
    { 
     return $repository->findElementsByAccount($this); 
    } 
} 

इस तरह इकाई को अभी भी भंडारों के बारे में पता नहीं है, लेकिन आप इसे किसी को पारित करने की अनुमति देते हैं।

जब यह लागू करने, ElementRepository विस्तार EntityRepository की जरूरत नहीं है, लेकिन inject निर्माण पर EntityRepository। इस तरह आप अभी भी अपनी इकाइयों को बदलने के बिना दृढ़ता परत को स्वैप कर सकते हैं।

+0

पहला समाधान काम करना चाहिए, लेकिन ऐसा आकर्षक नहीं लगता है क्योंकि फ़िल्टरिंग स्मृति में और एसक्यूएल में किया जाता है यदि मैं सही ढंग से समझता हूं। भंडार दृष्टिकोण के साथ, मैं 'सेक्शन' ऑब्जेक्ट्स मैन्युअल रूप से "संयोजन" करूँगा, है ना? यही है, तत्वों के माध्यम से स्वचालित रूप से प्राप्त किए जाने के बजाय तत्वों को स्वयं स्थापित करना। शायद [इस तरह कुछ] (http://pastebin.com/CWAadsbB)? उस स्थिति में, मैं यह सुनिश्चित करने के लिए इकाई पर एसोसिएशन को भी हटा सकता हूं कि गलत वर्गों को गलती से नहीं लाया जाता है यदि इसका उपयोग करने से पहले ऑब्जेक्ट को इकट्ठा नहीं किया जाता है। अच्छे अंक, वैसे! – Andy0708

+0

पहले समाधान के साथ डीबी परिभाषित संघों के आधार पर केवल इकाइयों को लाएगा। विशिष्ट 'खाता' पर फ़िल्टरिंग केवल PHP में ही की जाएगी। यही कारण है कि मेरा सुझाव है कि आप एक भंडार का उपयोग करें, क्योंकि आप डीबी को केवल उन्हीं तत्वों को प्राप्त कर सकते हैं जिन्हें आप वास्तव में चाहते हैं (इसलिए डीबी फ़िल्टरिंग करता है)। –

+1

मैंने एक उदाहरण क्वेरी के साथ अपना जवाब अपडेट कर दिया है। –

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