Php Symfony 3服务依赖于另一服务的未知数量。如何实施?

Php Symfony 3服务依赖于另一服务的未知数量。如何实施?,php,symfony,Php,Symfony,我对Symfony相当陌生,但对PHP很有经验。假设我有一个服务,它需要另一个未知数量的服务。注射它是没有意义的(我会注射多少)。我可以使用ContainerAwareInterface和ContainerAwareTrait,但我已经读到了这一点 稍微做作的例子: class ProcessBuilder { private $allCommands = []; public function build(array $config){ foreach ($c

我对Symfony相当陌生,但对PHP很有经验。假设我有一个服务,它需要另一个未知数量的服务。注射它是没有意义的(我会注射多少)。我可以使用
ContainerAwareInterface
ContainerAwareTrait
,但我已经读到了这一点

稍微做作的例子:

class ProcessBuilder {
    private $allCommands = [];

    public function build(array $config){
        foreach ($config => $command){
            $this->allCommands[] = $this->getContainer()->get('app.worker.command')->init($command);
        }
    }
}
在获取我的
ProcessBuilder
服务时,我不知道
$config
数组中将有多少项传递到
build()
。由于
命令
类(
app.worker.Command
服务)的工作方式,它们不能共享单个实例

最好的方法是什么?或者我需要走
containerware*
路线吗


我希望这是有道理的,谢谢你的帮助。如果以前有人问过这个问题,我很抱歉,但我在谷歌上搜索得很好,什么都没找到。

你走的方向是正确的。现在唯一正确的地方不见了

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

您可以使用扩展注册服务,并使用CompilerPass以任何方式操作它们

例子 这是

你的案子通过了吗 如果我们将您的代码转换为编译器过程,它将如下所示:

ProcessBuilder.php

class ProcessBuilder
{
    /**
     * @var CommandInterface[]
     */
    private $allCommands = [];

    public function addCommand(CommandInterface $command)  
    {
        $this->allCommands[] = $command
    }
}
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)]);
            }
        }
    }
}
use Symfony\Component\HttpKernel\Bundle\Bundle;

final class AppBundle extends Bundle
{
    public function build(ContainerBuilder $containerBuilder): void
    {
        $containerBuilder->addCompilerPass(new AddCommandsToProcessBuilderCompilerPass);
    }
}
addCommandStopProcessBuilderCompilerPass.php

class ProcessBuilder
{
    /**
     * @var CommandInterface[]
     */
    private $allCommands = [];

    public function addCommand(CommandInterface $command)  
    {
        $this->allCommands[] = $command
    }
}
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)]);
            }
        }
    }
}
use Symfony\Component\HttpKernel\Bundle\Bundle;

final class AppBundle extends Bundle
{
    public function build(ContainerBuilder $containerBuilder): void
    {
        $containerBuilder->addCompilerPass(new AddCommandsToProcessBuilderCompilerPass);
    }
}
AppBundle.php

class ProcessBuilder
{
    /**
     * @var CommandInterface[]
     */
    private $allCommands = [];

    public function addCommand(CommandInterface $command)  
    {
        $this->allCommands[] = $command
    }
}
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)]);
            }
        }
    }
}
use Symfony\Component\HttpKernel\Bundle\Bundle;

final class AppBundle extends Bundle
{
    public function build(ContainerBuilder $containerBuilder): void
    {
        $containerBuilder->addCompilerPass(new AddCommandsToProcessBuilderCompilerPass);
    }
}
并将包添加到
AppKernel.php

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

这是在Symfony以干净的方式完成您需要的全部过程。

也许这就是您需要的?或者另一种方法是。您应该传递命令服务工厂实例,而不是命令服务实例。这是一个非常干净的解决方案@dragoste-出于好奇,您的工厂如何知道要使用哪些服务?@Cerad问题是特定服务的实例数量可变,而不是要注入哪个服务。@dragoste我认为问题是如何将数量可变的命令服务注入某种命令调度程序。但我很可能错了。