Symfony如何在子类循环时使用自动装配

时间:2018-12-12 01:12:30

标签: symfony dependency-injection autowired

我有一个处理某些数据并循环通过多个“处理器”进行处理的命令。代码是这样的:

image

...

在循环中,我必须为处理器分配各种依赖关系,其中一些可以自动装配。在这种情况下,我将如何使用自动装配,因为我的类名称不是静态的,而是在数组中。

Symfony版本为4.1

1 个答案:

答案 0 :(得分:2)

在您的代码中有很多东西不是立即显而易见的,但是解决此问题的典型方法是使用“服务定位器”。 Docs

让我们假设您有一些实现接口Processor的服务:

界面:

interface Processor {
    public function process($file): void;
}

夫妇的实现:

class Foo implements Processor
{
    public function __construct(DataSetProvider $dataSet, ArchivalHashService $archivalHash, \Swift_Mailer $swift) {
        // initialize properties
    }

    public function process($file) {
        // process implementation
    }

    public static function getDefaultIndexName(): string
    {
        return 'candidateFileOne';
    }
}

夫妇的实现:

class Bar implements Processor
{
    public function __construct(\Swift_Mailer $swift, EntityManagerInterface $em) {
        // initialize properties
    }

    public function process($file) {
        // process implementation
    }

    public static function getDefaultIndexName(): string
    {
        return 'candidateFileTwo';
    }
}

请注意,每个处理器具有完全不同的依赖性,可以直接自动连接,并且每个处理器都有一个getDefaultIndexName()方法。

现在,我们将“标记”实现Processor接口的所有服务:

# services.yaml
services:
    # somewhere below the _defaults and the part where you make all classes in `src` available as services
    _instanceof:
        App\Processor:
            tags:
                - { name: "processor_services", default_index_method: 'getDefaultIndexName' }

注意:文档says(如果您定义了public static function getDefaultIndexName(),则默认情况下会选中该文档)。但是我发现此目前不起作用。但是,如果您定义default_index_method,则可以将其连接到您选择的方法。我暂时保留getDefaultIndexName,但您可以选择自己的东西。

现在,如果您需要在控制台命令中执行此过程,例如:

use Symfony\Component\DependencyInjection\ServiceLocator;

class MyConsoleCommand
{
    private ServiceLocator $locator;

    public function __construct(ServiceLocator $locator)
    {
        $this->locator = $locator;
    }

}

要注入服务定位器,您需要执行以下操作:

#services.yaml

services:
    App\HandlerCollection:
        arguments: [!tagged_locator { tag: 'processor_services' } ]

要从服务定位器中获取任何处理器,您将执行以下操作:

$fooProcessor = $this->locator->get('candidateFileOne');
$barProcessor = $this->locator->get('candidateFileTwo');

总结一下,基本上您需要的是:

  1. 定义处理器的共享接口
  2. 使用该界面标记所有处理器服务
  3. 为每个处理器定义一个getDefaultIndexName(),可帮助您将文件与处理器进行匹配。
  4. 在需要使用此服务的类中注入标记的服务定位符

您可以保留所有服务的自动连线。

注意:您可以使用抽象类而不是接口,其工作方式相同。我更喜欢使用界面,但这取决于您。

为了完整起见,here is a repo与上面的工作在Symfony 4.3上一样。