4

services.xmlファイル内のサービスの配列を表すパラメーターがあります。

<parameters>
    <parameter key="decorators.all" type="collection">
        <parameter type="service" id="decorator1" />
        <parameter type="service" id="decorator2" />
        <parameter type="service" id="decorator3" />
    </parameter>
</parameters>

<services>
    <service id="decorator1" class="\FirstDecorator" />
    <service id="decorator2" class="\SecondDecorator" />
    <service id="decorator3" class="\ThirdDecorator" />
</services>

次に、このコレクションをサービスの配列として別のサービスに挿入します。

<services>
    <service id="notifications_decorator" class="\NotificationsDecorator">
        <argument>%decorators.all%</argument>
    </service>
</services>

しかし、うまくいきません。理由がわかりません。私は何が欠けていますか?

4

4 に答える 4

5

したがって、サービスの配列ではなく、パラメーターの配列を注入します。次の方法でサービスごとにサービスを注入できます。

<services>
    <service id="notifications_decorator" class="\NotificationsDecorator">
        <argument type="service" id="decorator1"/>
        <argument type="service" id="decorator2"/>
        <argument type="service" id="decorator3"/>
    </service>
</services>

または(私の意見では、より良い方法で)サービスにタグ を付けて、コンパイルパス中にdecoratorsそれらを挿入します。notifications_decorator

更新: タグ付きサービスの操作

あなたの場合、次のようにサービスを変更する必要があります。

<services>
    <service id="decorator1" class="\FirstDecorator">
        <tag name="acme_decorator" />
    </service>
    <service id="decorator2" class="\SecondDecorator">
        <tag name="acme_decorator" />
    </service>
    <service id="decorator3" class="\ThirdDecorator">
        <tag name="acme_decorator" />
    </service>
</services>

decorators.allさらに、セクションからパラメーターを削除する必要があります<parameters>。次に、次の sth のようなaddDectorator関数を追加する必要があり\NotificationsDecoratorます。

class NotificationsDecorator
{
    private $decorators = array();

    public function addDecorator($decorator)
    {
        $this->decorators[] = $decorator;
    }
    // more code
}

のインターフェイスを作成し、これをfor関数decoratorの型として追加するとよいでしょう。$decoratoraddDecorator

次に、独自のコンパイラ パスを作成し、タグ付けされたサービスについて尋ね、このサービスを別のサービスに追加する必要があります (ドキュメントと同様):

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

class DecoratorCompilerPass implements CompilerPassInterface
{
    public function process(ContainerBuilder $container)
    {
        if (!$container->hasDefinition('notifications_decorator')) {
            return;
        }

        $definition = $container->getDefinition('notifications_decorator');
        $taggedServices = $container->findTaggedServiceIds('acme_decorator');

        foreach ($taggedServices as $id => $attributes) {
            $definition->addMethodCall(
                'addDecorator',
                array(new Reference($id))
            );
        }
    }
}

最後に、次のようにバンドル クラスDecoratorCompilerPassに toを追加する必要があります。Compiler

class AcmeDemoBundle extends Bundle
{
    public function build(ContainerBuilder $container)
    {
        parent::build($container);

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

幸運を!

于 2013-09-05T06:51:14.203 に答える
2

CompilerPassInterfaceタグ付けされたサービス(または必要なもの)と、メソッド呼び出しの代わりにサービスの配列を使用する少し異なるアプローチ。@NHGの回答との違いは次のとおりです。

<!-- Service definition (factory in my case) -->
<service id="example.factory" class="My\Example\SelectorFactory">
    <argument type="collection" /> <!-- list of services to be inserted by compiler pass -->
</service>

コンパイラパス:

/*
 * Used to build up factory with array of tagged services definition
 */
class ExampleCompilerPass implements CompilerPassInterface
{
    const SELECTOR_TAG = 'tagged_service';

    public function process(ContainerBuilder $container)
    {
        $selectorFactory = $container->getDefinition('example.factory');
        $selectors = [];
        foreach ($container->findTaggedServiceIds(self::SELECTOR_TAG) as $selectorId => $tags) {
            $selectors[] = $container->getDefinition($selectorId);
        }

        $selectorFactory->replaceArgument(0, $selectors);
    }
}
于 2015-06-16T08:15:38.067 に答える