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

イベントリスナーのドキュメントを参照してください。リスナーNavigationWorkerを作成してイベントを作成します。明示的に作成する必要はありません。

于 2012-11-14T18:34:35.327 に答える
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 に答える