Symfony 3服务依赖于未知数量的其他服务.如何实现


Symfony 3 service depends on unknown quantity of another service. How to implement?

我对Symfony相当陌生,但对PHP有经验。假设我有一个服务,它需要未知数量的另一个服务。注射它没有意义(我要注射多少)。我可以使用ContainerAwareInterfaceContainerAwareTrait,但我读到这不是一个好方法。

稍微做作的例子:

class ProcessBuilder {
    private $allCommands = [];
    public function build(array $config){
        foreach ($config => $command){
            $this->allCommands[] = $this->getContainer()->get('app.worker.command')->init($command);
        }
    }
}

当我获得ProcessBuilder服务时,我不知道传递给build()$config数组中有多少项。由于Command类(app.worker.command服务)的工作方式,它们不能共享单个实例。

最好的方法是什么?还是我需要走ContainerAware*路线?

我希望这是有意义的,谢谢你的帮助。对不起,如果这个问题之前已经问过了,但我有一个很好的谷歌,没有想出任何东西。

你的方向是对的现在只缺一个正确的位置。

收集某种类型的服务,我们需要提前一步。到依赖注入容器编译(这就是Symfony收集EventSubscriber类型或Voter类型服务的方式)

你可以用Extension注册服务,用CompilerPass以任何方式操作它们。

下面是如何收集A类型的所有服务,并通过setter将它们添加到B类型的服务的示例。

你的案例在编译器传递

如果我们将你的代码转换为编译器传递,它看起来像这样:

ProcessBuilder.php

class ProcessBuilder
{
    /**
     * @var CommandInterface[]
     */
    private $allCommands = [];
    public function addCommand(CommandInterface $command)  
    {
        $this->allCommands[] = $command
    }
}

AddCommandsToProcessBuilderCompilerPass.php

use Symfony'Component'DependencyInjection'Compiler'CompilerPassInterface;
final class AddCommandsToProcessBuilderCompilerPass implements CompilerPassInterface
{
    public function process(ContainerBuilder $containerBuilder): void
    {
        # using Symfony 3.3+ class name, you can use string name as well
        $processBuilderDefinition = $this->containerBuilder->getDefinition(ProcessBuilder::class);
        foreach ($this->containerBuilder->getDefinitions() as $serviceName => $definition) {
            if (is_subclass_of($definition->getClass(), CommandInterface::class)) {
                $processBuilderDefinition->addMethodCall('addCommand', [new Reference($serviceName)]);
            }
        }
    }
}

AppBundle.php

use Symfony'Component'HttpKernel'Bundle'Bundle;
final class AppBundle extends Bundle
{
    public function build(ContainerBuilder $containerBuilder): void
    {
        $containerBuilder->addCompilerPass(new AddCommandsToProcessBuilderCompilerPass);
    }
}

并将您的bundle添加到AppKernel.php:

final class AppKernel extends Kernel
{
    public function registerBundles()
    {
        bundles = [];
        $bundles[] = new AppBundle;
    }
}

这是在Symfony中以干净的方式完成所需操作的完整过程。