我对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
服务时,我不知道传递给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中以干净的方式完成所需操作的完整过程。