私はこれに答えを変えています。なぜなら、それは私を正しい道に導いたのですが、それは完全な答えではなかったからです。この記事では、実際には、事前定義されたカーネルイベントにのみフックすることができます。しかし、私は自分のものが必要だったので、そこから仕事を始めました。
結局、私は自分のタグ、それらのタスクを処理するためのコンパイラパスを作成することになりました。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を使用します)。
うまくいけば、それが似たようなことをしようとしている他の人の助けになるでしょう。