berny/tag-bundle

此包已被弃用且不再维护。没有建议的替代包。

帮助收集带有标签的服务并将它们注入其他服务

安装: 45

依赖: 1

建议者: 0

安全: 0

星标: 1

关注者: 3

分支: 1

开放问题: 0

类型:symfony-bundle

1.8 2016-06-01 16:24 UTC

This package is not auto-updated.

Last update: 2024-02-28 09:03:16 UTC


README

你是否厌倦了只为了收集容器上标记的服务而添加 CompilerPass

使用这个包说NO!

SensioLabsInsight

⚠ 注意 ⚠

注意命名空间的变化

  • 0.4.0 之前: Berny\Bundle\TagBundle
  • 0.4.0 之后: xPheRe\Bundle\TagBundle

为什么要这样做呢?

很多时候,你想要搜索带有特定标记的服务,并用它们调用你的服务中的某个方法。这可以通过自定义 CompilerPass 来完成。

services:
    my_plugin_enumerator:
        class: PluginEnumerator

    useless_plugin:
        class: UselessPlugin
        tag: - { name: my_plugin }

    even_more_useless_plugin:
        class: EvenMoreUselessPlugin
        tag: - { name: my_plugin }
class PluginEnumeratorConsumerCompilerPass implements CompilerPassInterface
{
    public function process(ContainerBuilder $container)
    {
        if (!$container->has('my_plugin_enumerator')) {
            return;
        }

        $definition = $container->findDefinition('my_plugin_enumerator');

        $taggedServices = $container->findTaggedServices('my_plugin');

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

另一个用例是将服务注入到所有带有特定标记的其他服务中。一个例子

services:
    my_event_dispatcher:
        class: MyEventDispatcher

    useless_service:
        class: UselessService
        tag: - { name: my_event_dispatcher.aware }

    even_more_useless_service:
        class: EvenMoreUselessService
        tag: - { name: my_event_dispatcher.aware }
class MyEventDispatcherInjectableCompilerPass implements CompilerPassInterface
{
    public function process(ContainerBuilder $container)
    {
        if (!$container->has('my_event_dispatcher')) {
            return;
        }

        $reference = new Reference('my_event_dispatcher');
        $taggedServices = $container->findTaggedServices('my_event_dispatcher.aware');

        foreach ($taggedServices as $id => $attributes) {
            $definition = $container->findDefinition($id);
            $definition->addMethodCall('setMyEventDispatcher', array($reference));
        }
    }
}

这个样板代码在每个我见过的项目中一次又一次地重复。使用这个包,你可以告别大多数这种编译器传递。

特性

使用这个包,你可以

  • 将一个服务标记为另一个标签的消费者。
  • 将一个服务标记为可注入到其他服务中。

兼容性

在Symfony2下测试,从2.0.10到2.6.3

安装

composer/packagist

  • 在composer中需要 xphere/tag-bundle
  • 将包添加到你的 AppKernel.php

使用

消费者

你可以定义一个服务作为另一个标签的“标签消费者”,并让包建立它们之间的关系。只需将你的服务标记为 tag.consumer,并指定要收集哪个 tag 以及要调用哪个 method

使用此包的第一个例子只是配置

services:
    my_plugin_enumerator:
        class: PluginEnumerator
        tags:
            - { name: tag.consumer, tag: my_plugin, method: addPlugin }

    useless_plugin:
        class: UselessPlugin
        tag: - { name: my_plugin }

    even_more_useless_plugin:
        class: EvenMoreUselessPlugin
        tag: - { name: my_plugin }

唯一的更改是 my_plugin_enumerator 中的标签。编译器传递的样板代码消失了。

这会对每个 my_plugin 调用 PluginEnumerator::addPlugin,但你也可以通过 bulk 参数一次性调用所有服务。

services:
    my_plugin_enumerator:
        class: PluginEnumerator
        tags:
            - { name: tag.consumer, tag: my_plugin, method: addPlugins, bulk: true }

这仅通过一个数组调用 PluginEnumerator::addPlugins,包含服务。

要使服务通过其构造函数通过它消费其依赖项,只需省略标签中的 method 属性。

可注入

你可以定义一个服务作为来自另一个标签的“标签可注入”服务,并让包做艰苦的工作。只需将你的服务标记为 tag.injectable,并指定要收集哪个 tag 以及每个服务中要调用哪个 method

介绍中的第二个例子将像这样

services:
    my_event_dispatcher:
        class: MyEventDispatcher
        tag: - { name: tag.injectable, tag: my_event_dispatcher.aware, method: setMyEventDispatcher }

    useless_service:
        class: UselessService
        tag: - { name: my_event_dispatcher.aware }

    even_more_useless_service:
        class: EvenMoreUselessService
        tag: - { name: my_event_dispatcher.aware }

唯一的更改是 my_event_dispatcher 中的标签。编译器传递的样板代码也消失了。

这迫使所有 my_event_dispatcher.aware 都有一个 setMyEventDispatcher 方法。但你可以通过 method 参数为特定服务更改它。

services:
    my_event_dispatcher:
        class: MyEventDispatcher
        tag: - { name: tag.injectable, tag: my_event_dispatcher.aware, method: setMyEventDispatcher }

    useless_service:
        class: UselessService
        tag: - { name: my_event_dispatcher.aware, method: setEventDispatcher }

    even_more_useless_service:
        class: EvenMoreUselessService
        tag: - { name: my_event_dispatcher.aware }

现在它正在为 UselessService 调用 setEventDispatcher,而对于其他服务则是默认方法。

高级使用

这些都是基础知识,虽然还有更多选项可供您对依赖项进行主要控制。

顺序

您可以使用每个标签中的 order 字段指定服务将按什么顺序注入到消费者中。较低的顺序优先于较高的顺序。未指定顺序的标记服务将在已排序服务之后注入。如果顺序之间有冲突,则保持symfony声明顺序。

索引批量服务

当批量活动时,您可以使用 key 指定用于索引每个标签的值,而不是使用纯数组。

services:
    my_command_bus:
        class: MyCommandBus
        tags:
            - { name: tag.consumer, tag: my_command_handler, bulk: true, key: handles }

    my_class_command_handler:
        class: MyClassCommandHandler
        tag: - { name: my_command_handler, handles: MyClass }

    other_class_command_handler:
        class: OtherClassCommandHandler
        tag: - { name: my_command_handler, handles: OtherClass }

这会导致以下注入

[
    'MyClass' => new MyClassCommandHandler(),
    'OtherClass' => new OtherClassCommandHandler(),
]

您也可以在消费者定义中使用multiple字段指定多个元素将具有相同的索引,并且需要收集数组而不是单个服务。

services:
    my_event_bus:
        class: MyEventBus
        tags:
            - { name: tag.consumer, tag: my_event_handler, bulk: true, key: listensTo, multiple: true }

    first_event_handler:
        class: FirstEventHandler
        tag: - { name: my_event_handler, listensTo: MyEvent }

    second_event_handler:
        class: SecondEventHandler
        tag: - { name: my_event_handler, listensTo: OtherEvent }

    third_event_handler:
        class: ThirdEventHandler
        tag: - { name: my_event_handler, listensTo: MyEvent }

这会导致以下注入

[
    'MyEvent' => [
        new FirstEventHandler(),
        new ThirdEventHandler(),
    ],
    'OtherEvent' => [
        new SecondEventHandler(),
    ],
]

如果指定了,Multiple也会尊重排序。

参考

通常,依赖项会直接注入到您的服务中,但您可以通过在消费者定义中将字段reference设置为false,将依赖项作为服务ID注入。

instanceof

您可以通过在消费者定义中使用字段instanceof强制依赖项成为某个类或接口的实例。

无包

您可以在不添加“整个”包到您的Kernel的情况下手动添加TagConsumerPassTagInjectablePass(或两者),甚至可以自定义用于应用它们的标签名称。

在您的Kernel

[...]
use xPheRe\Bundle\TagBundle\DependencyInjection\Compiler\TagConsumerPass;
use xPheRe\Bundle\TagBundle\DependencyInjection\Compiler\TagInjectablePass;
[...]

class AppKernel extends Kernel
{
    [...]
    protected function prepareContainer(ContainerBuilder $container)
    {
        parent::prepareContainer($container);

        $container->addCompilerPass(new TagConsumerPass('tag_collector'));
        $container->addCompilerPass(new TagInjectablePass('tag_injectable'));
    }
    [...]
}