是否可以在yaml定义(services.yaml)中动态访问Symfony服务的类名?



问题

我有很多班级都是家长班的孩子。

我用来实例化这些子级的工厂有一个参数,一个字符串,它是子级的类名,这样工厂就可以知道要构建什么。

希望

我真的很想一下子解决所有这些孩子的自动布线问题。我知道在services.yaml中,我可以通过通配符资源递归地定义类的自动布线配置,例如:

...
Namespace:
resource: '..namespace/foo/*'
# configuration for all classes in the 'foo' directory
autowire: true
public: true

然而,对于我的子目录,这些子目录需要用接受其类名的工厂方法进行实例化,这似乎是不可能的。

我希望能够做的是类似于这样的事情$1〃;相当于许多基于正则表达式的解决方案所允许的匹配通配符的字符串(包括一些通过Symfony本身可用的字符串,如路由(:

...
DomainChildren:
resource: '../domain/children/*'
factory: ['DomainFactory', 'getChild']
arguments: 'DomainChildren$1'

然而,可以理解的是,这并不奏效。

我知道存在一些形式为";表达式";,和!tagged_iterator(在这里看到(,所以我怀疑可能有一些东西可以做我想要做的事情,因为关于这两个例子的文档相当分散。我一直找不到一种方法来动态地获取类的名称作为其服务定义的一部分,希望它存在,但我就是找不到它

我尝试的另一件事是给子类一个函数,该函数可以按预期实例化它,接受工厂类的参数,但我找不到定义一个相对函数来代替工厂的方法。使用类的构造函数以外的任何东西的唯一方法似乎是显式声明类和函数对。

澄清

我不接受任何涉及重构的答案,这些类是如何构建的,与我在这里引用的工厂有关。这个问题不是";这种设计是个好主意吗;,我们已经远远超过了这一点——这是我一直坚持的设计。

问题摘要

在定义通用自动布线配置时,有没有任何方法可以实现我的目标,即动态引用类的名称(而不是在yaml中使用显式字符串(?

我不知道如何使用yaml做这类事情,但编译过程很容易,可能更容易理解。我用一个新的Symfony 6应用程序开发了这个,但它在早期版本上应该可以正常工作。对默认的services.yaml文件没有任何更改。

从域对象开始,只是为了确保我们在同一页上。

namespace AppDomain;
abstract class MyParent {}
class Child1 extends MyParent {}
class Child2 extends MyParent {}
class MyFactory
{
public function getChild(string $childClass) : MyParent
{
// Just ignore the 'something' for now
return new $childClass('something');
}
}

此时,如果您运行bin/console debug:container Child1,您将看到有一个Child1服务,但它没有使用MyFactory。

现在我们添加编译过程:

# src/Kernel.php
# implement CompilerPassInterface
class Kernel extends BaseKernel implements CompilerPassInterface
{
use MicroKernelTrait;
// Tag all the child classes
protected function build(ContainerBuilder $container)
{
$container->registerForAutoconfiguration(MyParent::class)
->addTag('my.child');
}
public function process(ContainerBuilder $container)
{
$factoryReference = new Reference(MyFactory::class);
// Grab all the child services
$childClasses = array_keys($container->findTaggedServiceIds('my.child'));
foreach ($childClasses as $childClass) {
// And replace with a factory service
$definition = new Definition($childClass);
$definition->setFactory([$factoryReference,'getChild'])
->setArgument(0,$childClass);
$container->setDefinition($childClass,$definition);
}
//dump($childClasses);
}
}

现在,当我们检查Child1服务时,我们可以看到它像我们想要的那样调用MyFactory::getChild。我做了一个测试注入的命令:

class ChildCommand extends Command
{
public function __construct(private Child1 $child1, private Child2 $child2)
{
parent::__construct();
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);

$io->success('Child1 ' . get_class($this->child1));
$io->success('Child2 ' . get_class($this->child2));
return Command::SUCCESS;
}
}

一切似乎都如预期的那样。

我有点担心构造函数的争论。我假设你的子类可能有一些,我不确定autowire是否会引起问题。所以我加了一个:

class Child2 extends MyParent
{
public function __construct(private string $something)
{

}
}

这一切似乎又一次奏效了。当然,注入任何必要的依赖关系将取决于您的工厂类。

享受

相关内容

最新更新