1

问题很简单,但是......所以我们有主要服务:

class ManagerOne {}

并有几个我们想在主要服务中使用的其他服务:

class ServiceOne{}
class ServiceTwo{}
class ServiceThree{}
class ServiceFour{}
...

每个命名为(在 services.yml 中)

service.one
service.two
service.three
service.four
...

服务的位置不同,而不是在一个文件夹中(但我认为自定义自动加载器不是一个大麻烦)。

关于手册,我们可以通过主服务(ManagerOne)中的 __construct() 注入它们,但是如果我们需要注入 20 个这样的服务呢?或者只使用我们需要的。在服务中将它们描述为简单的注入?哦,我认为这不是一个好主意......我们也可以注入容器,就是这样。但!到处都有人说注入容器是最糟糕的解决方案。

我想要的是。我需要ManagerOne服务的方法,它将通过'service.name'或'path'加载我需要的服务,并带有检查器'服务存在'。

4

2 回答 2

4

您可以使用服务标记并标记您想在ManagerOne班级中使用的每个服务。并且要么使用构造函数依赖注入或方法注入。

例子:

首先,您需要一个编译器通行证来收集您的标记服务:

namespace ...\DependencyInjection\Compiler;

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

class ExamplePass implements CompilerPassInterface
{

    public function process(ContainerBuilder $container)
    {
        if (!$container->hasDefinition("manager.one")) {
            return;
        }
        $services = array();
        foreach ($container->findTaggedServiceIds('managed_service') as $serviceId => $tag) {
            $alias = isset($tag[0]['alias'])
                ? $tag[0]['alias']
                : $serviceId;

            // Flip, because we want tag aliases (= type identifiers) as keys
            $services[$alias] = new Reference($serviceId);
        }
        $container->getDefinition('manager.one')->replaceArgument(0, $services);
    }
}

然后你需要将编译器传递添加到你的包类:

namespace Example\ExampleBundle;

use Symfony\Component\HttpKernel\Bundle\Bundle;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use ...\DependencyInjection\Compiler\ExamplePass;

class ExampleBundle extends Bundle
{
    public function build(ContainerBuilder $container)
    {
        parent::build($container);
        $container->addCompilerPass(new ExamplePass());
    }
}

现在您可以使用您的服务:

# services.yml
manager.one:
    class: ManagerClass
    arguments:
        - [] # will be replaced by our compiler pass

services.one:
    class: ServiceOne
    tags:
        - { name: managed_service, alias: service_one }

services.two:
    class: ServiceTwo
    tags:
        - { name: managed_service, alias: service_two }

但请注意,如果您有经理,所有服务类都将自动创建。如果这对您来说是一个性能缺陷,您可以只将服务 ID(而不是参考)传递给您的管理类。添加@service_containeras 第二个参数并根据需要创建服务。

于 2013-05-19T07:59:45.527 回答
0

自 2017 年以来,Symfony 3.3Symplify\PackageBuilder这变得更加容易。

多亏了这个软件包,您可以:

  • 丢弃标签
  • CompilerPass在字符串上使用严格类型的简单 5 行

让我们来看看你的例子


假设你有

  • 1 名经理 -UpdateManager班级
  • 许多更新程序 - 一个实现的类UpdaterInterface

1. 使用PSR-4 自动发现的服务配置

# app/config/services.yml
services:
    _defaults:
        autowire: true

    App\:
        resource: ../../src/App

2. 收集编译器通行证

use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symplify\PackageBuilder\Adapter\Symfony\DependencyInjection\DefinitionCollector;

final class CollectorCompilerPass implements CompilerPassInterface
{
    public function process(ContainerBuilder $containerBuilder)
    {
        DefinitionCollector::loadCollectorWithType(
            $containerBuilder,
            UpdateManager::class,
            UpdaterInterface::class,
            'addUpdater'
        );
    }
}

它收集所有UpdaterInterface类型的服务并通过addUpdater()方法将它们添加到UpdateManager.

3.在Bundle中注册Compiler Pass

namespace App;

use Symfony\Component\HttpKernel\Bundle\Bundle;
use Symfony\Component\DependencyInjection\ContainerBuilder;

final class UpdaterBundle extends Bundle
{
    public function build(ContainerBuilder $container)
    {
        $container->addCompilerPass(new CollectorCompilerPass);
    }
}

就这样!

如何添加新的更新程序?

只需创建类,实现UpdaterInterface并将其加载到UpdateManager.

  • 没有标签
  • 无需手动服务注册
  • 没有无聊的工作

享受!

于 2017-10-21T19:31:29.737 回答