1

我有一个类,用于从各种互连的捆绑包中生成导航。我有一个导航服务来完成这个。

为了将此服务与导航的其他位连接,我希望允许其他捆绑包定义自己的服务,然后侦听事件侦听器并在适当的时间添加其导航项。

问题是,如果不先手动调用该服务以创建它,我无法弄清楚如何让服务监听事件。

有任何想法吗?


为了给出更具体的想法,我有这样的事情:

// 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 并让它添加他们的侦听器,但这非常难看。

4

2 回答 2

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 }

编译器通行证:

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)。

希望这会帮助其他想要做类似事情的人。

于 2012-11-29T22:08:39.723 回答
1

请参阅事件侦听器文档。MakeNavigationWorker和事件监听器,它不需要显式构造。

于 2012-11-14T18:34:35.327 回答