Symfony2 डायनामिक ईवेंट सदस्यता

शुभ दोपहर हरजाईटेल।

नहीं तो बहुत पहले मैं एक गैर-मानक कार्य में भाग गया था, मैं इसके समाधान का एक संस्करण साझा करना चाहता हूं, साथ ही साथ इस विषय पर स्मार्ट साबुन भी सीखना चाहता हूं। कौन परवाह करता है, बिल्ली में आपका स्वागत है।

प्रोजेक्ट कॉन्फ़िगरेशन के बारे में कुछ शब्द: सिम्फनी 2.3 + सिद्धांत 2 का उपयोग करना

समस्या का बयान

एक ईवेंट हैंडलर लागू करें जिसका नाम पहले से ज्ञात नहीं है। सिस्टम में डेटाबेस की सभी घटनाओं की एक रजिस्ट्री शामिल है।

प्रलेखन का उपयोग करते हुए, आप सिम्फनी ईवेंट डिस्पैचर के मानक कार्यान्वयन में घटनाओं की सदस्यता के लिए 3 विकल्पों को भेद सकते हैं।
  1. DI कंटेनर में kernel.event_listener टैग के साथ एक सेवा जोड़ें
  2. DI कंटेनर में EventSubscriberInterface को लागू करने के लिए kernel.event_subscriber टैग के साथ एक सेवा जोड़ें
  3. एप्लिकेशन प्रेषण के दौरान AddListener विधि कॉल जोड़ें


पहली विधि हमें शोभा नहीं देती है क्योंकि हम पहले से घटनाओं के नाम नहीं जानते हैं। दूसरी विधि आपको वही लगती है, जिसकी आपको आवश्यकता है, लेकिन getSubscribedEvents विधि स्थिर होनी चाहिए, जिसका अर्थ है कि यह इंजेक्शन सेवाओं के साथ बातचीत नहीं कर सकता है।

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

संकलक पास का उपयोग करने का विचार आया, इसलिए उसने इस समस्या को हल किया। बंडल ऑब्जेक्ट का एक उदाहरण बनाते समय, हम एक वर्ग को पंजीकृत कर सकते हैं जो DI कंटेनर को संकलित करने में भाग लेंगे। एक महत्वपूर्ण सवाल यह है कि सिद्धांत तक कैसे पहुंचा जाए, और यहां संकलक पासिंग बचाव के लिए आता है। कंटेनर संकलित करने के लिए 5 चरण हैं:


प्रारंभिक चरणों में, सेवा परिभाषाओं को संकलित किया जाता है, बाद के चरणों में अप्रयुक्त सेवाओं और निजी उपनामों को प्रलेखन से हटा दिया जाता है।

कार्यान्वयन

अंतिम चरण हमें जैसा दिखता है, क्योंकि इस स्तर पर सभी सेवाएं पहले से ही उपयोग के लिए तैयार हैं। हमारे संकलक की घोषणा करें:
/** * Builds the bundle. * * It is only ever called once when the cache is empty. * * This method can be overridden to register compilation passes, * other extensions, ... * * @param ContainerBuilder $container A ContainerBuilder instance */ public function build(ContainerBuilder $container) { parent::build($container); $container->addCompilerPass(new EventsCompilerPass(), PassConfig::TYPE_AFTER_REMOVING); } 


और वास्तव में संकलक कोड (डेटाबेस से डेटा प्राप्त करने के लिए जिम्मेदार कोड भंडार वर्ग में छिपा हुआ है)
  public function process(ContainerBuilder $container) { if (!$container->hasDefinition(self::SERVICE_KEY)) { return; } $eventClassName = $container->getParameter(self::EVENT_ENTITY_CLASS_PARAM); $dispatcher = $container->getDefinition(self::DISPATCHER_KEY); $em = $container->get('doctrine.orm.entity_manager'); $eventNames = array(); if ($this->isSchemaSynced($em, $eventClassName) !== false) { $eventNames = $em->getRepository($eventClassName) ->getEventNames(); } foreach ($eventNames as $eventName) { $dispatcher->addMethodCall( 'addListenerService', array($eventName['name'], array(self::SERVICE_KEY, 'process')) ); } } 

जैसा कि आप देख सकते हैं, हम सभी ईवेंट नामों का चयन करते हैं और ईवेंट डेटा हैंडलर के रूप में हमारी सेवा को पंजीकृत करते हैं। केवल अस्पष्टीकृत छोड़ दी गई चीज है ischemaSynced विधि, हम इसके कार्यान्वयन से शुरू करते हैं:
  protected function isSchemaSynced(EntityManager $em, $className) { $tables = $em->getConnection()->getSchemaManager()->listTableNames(); $table = $em->getClassMetadata($className)->getTableName(); return array_search($table, $tables); } 

यह जाँचता है कि क्या घटना के नाम वाली तालिकाएँ बनाई गई हैं। बात यह है कि पहली बार कंटेनर संकलित किया जाता है जब सिद्धांत: स्कीमा: क्रिएट सर्विस कमांड को कहा जाता है, और यह डीबीएक्ससेप्शन का कारण बन सकता है।

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

Source: https://habr.com/ru/post/In187178/


All Articles