Events Symfony2从服务中正确地挂接事件
我有一个类,用于从各种相互连接的bundle生成导航。我有一个导航服务来完成这项工作 为了将此服务与其他导航位连接起来,我希望允许其他bundle定义它们自己的服务,这些服务随后侦听事件侦听器,并在适当的时间添加它们的导航项 问题是,如果不先手动调用服务来创建它,我就不知道如何让服务侦听事件 有什么想法吗Events Symfony2从服务中正确地挂接事件,events,symfony,service,Events,Symfony,Service,我有一个类,用于从各种相互连接的bundle生成导航。我有一个导航服务来完成这项工作 为了将此服务与其他导航位连接起来,我希望允许其他bundle定义它们自己的服务,这些服务随后侦听事件侦听器,并在适当的时间添加它们的导航项 问题是,如果不先手动调用服务来创建它,我就不知道如何让服务侦听事件 有什么想法吗 为了给出更具体的想法,我有这样的想法: // Set up as a service in the bundle. class Navigation { // ... pro
为了给出更具体的想法,我有这样的想法:
// 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,并让它添加他们的侦听器,但这非常难看。请参阅。生成
NavigationWorker
和事件侦听器,它不需要显式构造。我正在更改此问题的答案,因为虽然这使我走上了正确的道路,但它不是完整的答案。那篇文章实际上只允许您连接到预定义的内核事件。但是我需要我自己的,所以我从那里开始工作
最后,我创建了自己的标记,一个用于处理这些任务的编译器传递。我还添加了自己的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)
现在EventListener将为每个事件添加侦听器。您不仅可以完全按照预期实现其他所有功能(导航也会发送它所侦听的事件)。您可以从任何捆绑包中钩住新的事件侦听器,它们甚至不需要共享公共类/接口
这也适用于任何自定义事件,只要具有事件常量的对象在结尾处的参数“.event.class”中注册(因此my_bundle.navigation.generate查找参数my_bundle.navigation.event.class,使用该类和常量generate)
希望这能帮助其他想做类似事情的人
public function build(ContainerBuilder $container)
{
parent::build($container);
$container->addCompilerPass(new EventListenerCompilerPass());
}