在我的控制器中获取标记服务列表

Getting a list of tagged services in my controller

我想要的是将服务添加到我以后要在我的控制器或服务中使用的服务容器。

所以我用自定义标签创建了两个服务 fbeen.admin

他们在这里:

services:
    app.test:
        class: AppBundle\Admin\TestAdmin
        tags:
            - { name: fbeen.admin }

    fbeen.admin.test:
        class: Fbeen\AdminBundle\Admin\TestAdmin
        tags:
            - { name: fbeen.admin }

现在我想在我的控制器中使用带有标签 fbeen.admin 的所有服务,但我不知道如何。

我遵循了 How to work with service tags 教程,但我被这条规则卡住了:

$definition->addMethodCall('addTransport', array(new Reference($id)));

在某种程度上应该调用 TransportChain class 的 addTransport 方法,但似乎没有被调用。

即使它被调用,我的控制器中仍然没有带有 fbeen.admin 标签的服务列表。

我确定我遗漏了什么,但谁能解释一下是什么?

p.s。我知道 compilerPass 在构建时运行,但例如 sonata admin 知道所有 admin classes 并且 twig 知道所有 twig 扩展。他们怎么知道的?

感谢您阅读本文:-)

这里要注意的是:CompilerPass 不会 运行 编译器传递本身中的 'addTransport'(或任何你可以称呼它的东西)——只是说 'when the time is right - run $definition->addTransport(...) class, with this data'.寻找发生这种情况的地方是在您的缓存目录 (grep -R TransportChain var/cache/) 中,它设置了 $transportChain->addTransport(...).

当您第一次使用该服务时 - 只有在 class 从容器实例化时才会填充数据。

Symfony 3.3

容器编译一次(在调试中更频繁,但在生产中只编译一次)。您使用 addMethodCall... 管理的是,一旦您从存储在 $definition 中的容器请求服务(在本例中为控制器)。然后容器将在初始化您的服务期间调用方法 addMethodCall('method'..

它在容器中的外观:

// This is pseudo content of compiled container
$service = new MyController();
// This is what compiler pass addMethodCall will add, now its your 
// responsibility to implement method addAdmin to store admins in for 
// example class variable. This is as well way which sonata is using
$service->addAdmin(new AppBundle\Admin\TestAdmin());
$service->addAdmin(new AppBundle\Admin\TestAdmin());

return $service; // So you get fully initialized service

Symfony 3.4+

你能做的是:

// Your services.yaml
services:
    App/MyController/WantToInjectSerivcesController:
        arguments:
            $admins: !tagged fbeen.admin

// Your controller
class WantToInjectSerivcesController {
    public function __construct(iterable $admins) {
        foreach ($admins as $admin) {
            // you hot your services here
        }
    }
}

您的服务的自动标记奖励。假设您所有的控制器都实现了接口 AdminInterface.

// In your extension where you building container or your kernel build method
$container->registerForAutoconfiguration(AdminInterface::class)->addTag('fbeen.admin');

这将自动标记所有使用标记实现您的接口的服务。所以你不需要明确地设置标签。

这对我有用:

使用 getTransports 方法扩展 TransportChain class:

public function getTransports()
{
    return $this->transports;
}

并在我的控制器中使用 TransportChain 服务:

use AppBundle\Mail\TransportChain;

$transportChain = $this->get(TransportChain::class);
$transports = $transportChain->getTransports();
// $transports is now an array with all the tagged services

谢谢 Alister Bulman 推动我前进:-)