2012-11-14 26 views
1

我有一個類用於從各種相互關聯的包中生成導航。我有一個導航服務來完成這個。Symfony2正確地處理來自服務的事件

爲了連接這個服務和導航的其他位,我想讓其他bundle定義他們自己的服務,然後監聽事件監聽器並在適當的時候添加他們的導航項。

問題是,我無法弄清楚如何讓一個服務監聽一個事件,而無需手動調用該服務來創建它。

任何想法?


爲了給出一個更加具體的想法,我有這樣的事情:

// Set up as a service in the bundle. 
class Navigation { 
    // ... 
    protected $dispatcher; // event dispatcher passed in to service 

    // ... 
    public function generateNavigation() { 
     $items = array(); 
     // add some items 

     $event = new NavigationEvent($items); // custom event 
     $this->eventDispatcher->dispatchEvent('navigation_event', $event); 
    } 
} 

// Set up as a service in some secondary bundle. 
class NavigationWorker { 
    /** 
    * @param $dispatcher Same instance as Navigation 
    */ 
    public function __construct(EventDispatcher $dispatcher) { 
     $dispatcher->addListener('navigation_event', array($this, 'doSomething')); 
    } 
} 

有了這個設置,它應該工作,如果NavigationWorker被稱爲在某些時候和構造的,但我可以並不總是直接調用它們,所以它永遠不會被構造,而且偵聽器也不會被添加。

我目前的做法是將所有NavigationWorkers傳遞給Navigation並讓它添加它們的監聽器,但這非常醜陋。

回答

1

我正在改變這個答案,因爲雖然這使我在正確的道路上,這不是完整的答案。那篇文章實際上只允許你插入預定義的內核事件。然而我需要我自己的,所以我開始從那裏回來工作。最後,我最終創建了自己的標籤,一個編譯器傳遞來處理這些任務。我還添加了我自己的EventDispatcher擴展,儘管這不是非常必要的(您可以使用正常的擴展)。

以下是文件解決方案的樣子。

配置:

parameters: 
    my_bundle.navigation.event.class: My\Bundle\DependencyInjection\NavigationEvent 

    my_bundle.event_dispatcher.class: My\Bundle\DependencyInjection\EventDispatcher 
    my_bundle.navigation.class: My\Bundle\DependencyInjection\NavigationGenerator 
    my_bundle.navigation_listener1.class: My\Bundle\DependencyInjection\NavigationListener 
    my_bundle.navigation_listener2.class: My\Bundle\DependencyInjection\NavigationListener 

services: 
    my_bundle.event_dispatcher: 
     class: %my_bundle.event_dispatcher.class% 
    my_bundle.navigation: 
     class: %my_bundle.navigation.class% 
     arguments: 
      - @my_bundle.event_dispatcher 
    my_bundle.navigation_listener1.class: 
     class: %my_bundle.navigation_listener1.class% 
     tags: 
      - { name: my_bundle.event_listener, event: my_bundle.navigation.generate, method: onGenerateNavigation } 
    my_bundle.navigation_listener2.class: 
     class: %my_bundle.navigation_listener2.class% 
     tags: 
      - { name: my_bundle.event_listener, event: my_bundle.navigation.generate, method: onGenerateNavigation } 

CompilerPass:

use Symfony\Component\DependencyInjection\ContainerBuilder; 
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; 
use Symfony\Component\DependencyInjection\Reference; 

class EventListenerCompilerPass implements CompilerPassInterface 
{ 
    public function process(ContainerBuilder $container) 
    { 
     if (!$container->hasDefinition('my_bundle.event_dispatcher')) { 
      return; 
     } 

     $definition = $container->getDefinition(
      'my_bundle.event_dispatcher' 
     ); 

     $taggedServices = $container->findTaggedServiceIds(
      'my_bundle.event_listener' 
     ); 

     foreach ($taggedServices as $id => $tagAttributes) { 
      foreach ($tagAttributes as $attributes) { 
       $definition->addMethodCall(
        'addListener', 
        array($this->getEventString($attributes['event'], $container),  array(new Reference($id), $attributes['method'])) 
       ); 
      } 
     } 
    } 

protected function getEventString($str, ContainerBuilder $container) 
{ 
    preg_match('/(.*)\.([^.]*)$/', $str, $matches); 
    $parameterName = $matches[1]; 
    $constName = strtoupper($matches[2]); 

    $eventClass = $container->getParameter($parameterName . '.event.class'); 

    if (!$eventClass) { 
     throw new Exception('Unable to find parameter: ' . $eventClass . '.event.class'); 
    } 

    // Return the value of the constant. 
    return constant($eventClass . '::' . $constName); 
} 

添加這樣的功能,以您的編譯器類(類似MyBundleBundle)。

public function build(ContainerBuilder $container) 
{ 
    parent::build($container); 

    $container->addCompilerPass(new EventListenerCompilerPass()); 
} 

現在,EventListener將爲每個事件添加偵聽器。你不是完全按照你所期望的來實現其他所有事情(導航也會分派它也監聽的事件)。你可以比任何包綁定新的事件監聽器,而且他們甚至不需要共享一個通用的類/接口。

這也適用於任何自定義事件,只要具有事件常量的對象在最後的「.event.class」參數中註冊(因此my_bundle.navigation.generate會查找參數my_bundle.navigation.event.class,使用該類和常量GENERATE)。

希望這會幫助其他人尋找類似的東西。

相關問題