Php 在子类中循环时如何使用自动连接?
我有一个sumfony4.3命令,它处理一些数据,并通过多个“处理器”循环进行处理。该代码使用一个工厂(自动连线),然后该工厂实例化该命令Php 在子类中循环时如何使用自动连接?,php,symfony,dependency-injection,symfony4,symfony-dependency-injection,Php,Symfony,Dependency Injection,Symfony4,Symfony Dependency Injection,我有一个sumfony4.3命令,它处理一些数据,并通过多个“处理器”循环进行处理。该代码使用一个工厂(自动连线),然后该工厂实例化该命令 use App\Entity\ImportedFile; use App\Service\Processor\Processor; class Factory implements FactoryInterface { /** @var array */ private $processors; /** @var TestCla
use App\Entity\ImportedFile;
use App\Service\Processor\Processor;
class Factory implements FactoryInterface
{
/** @var array */
private $processors;
/** @var TestClausesInterface */
private $testClauses;
private $em;
private $dataSetProvider;
private $ndviFromNasaService;
private $archivalHashService;
private $mailer;
private $projectDir;
public function __construct(
TestClausesInterface $testClauses,
ValidProcessorList $processors,
EntityManagerInterface $em,
DataSetProvider $dataSetProvider,
NDVIFromNasaService $ndviFromNasaService,
ArchivalHashService $archivalHashService,
\Swift_Mailer $mailer,
$projectDir)
{
$this->processors = $processors;
$this->testClauses = $testClauses;
$this->em = $em;
$this->dataSetProvider = $dataSetProvider;
$this->ndviFromNasaService = $ndviFromNasaService;
$this->archivalHashService = $archivalHashService;
$this->mailer = $mailer;
$this->projectDir = $projectDir;
}
public function findProcessorForFile(ImportedFile $file)
{
...
if ($found){
$candidates = $this->recursive_scan( $this->projectDir.'/src/Processor');
foreach ($candidates as $candidate){
if (substr($candidate,0,strlen('Helper')) === 'Helper'){
continue;
}
try {
$candidate = str_replace($this->projectDir.'/src/Processor/', '', $candidate);
$candidate = str_replace('/','\\', $candidate);
$testClassName = '\\App\\Processor\\'.substr( $candidate, 0, -4 );
/* @var Processor $test */
if (!strstr($candidate, 'Helper')) {
$test = new $testClassName($this->testClauses, $this->em, $this->dataSetProvider, $this->ndviFromNasaService, $this->archivalHashService, $this->mailer, $this->projectDir);
}
然而,我仍然必须:
- 自动连接工厂和处理器顶级中的所有参数
- 将所有参数按正确顺序传递给处理器
EntityInterface
,但只有少数使用SwiftMailer
和其他依赖项
由于我正在添加仅由少数处理器使用的服务,因此我正在寻找一种仅在处理器级别自动连接这些参数的方法。理想情况下,也无需向services.yml添加服务定义
总之,我希望能够向
处理器的任何子类添加依赖项,即使它是其他子类的父类并自动注入依赖项。在您的代码中有很多不明显的地方,但解决这一问题的典型方法是使用“服务定位器”
假设您有几个服务实现接口处理器:
界面:
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()
方法
现在,我们将“标记”所有实现处理器
接口的服务:
interface Processor {
public function process($file): void;
}
#services.yaml
服务:
#在_默认值下面的某个地方,以及使“src”中的所有类作为服务可用的部分
_实例:
应用程序\处理器:
标签:
-{name:“处理器\服务”,默认\索引\方法:'getDefaultIndexName'}
注意这里:如果您定义了一个公共静态函数getDefaultIndexName()
,那么默认情况下会选择它。但我发现这个目前不起作用。但是如果您定义了default\u index\u方法
,则可以将其连接到您选择的方法。我暂时保留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');
要从服务定位器获取任何处理器,请执行以下操作:
#services.yaml
services:
App\HandlerCollection:
arguments: [!tagged_locator { tag: 'processor_services' } ]
$fooProcessor = $this->locator->get('candidateFileOne');
$barProcessor = $this->locator->get('candidateFileTwo');
总而言之,基本上你需要的是:
为处理器定义一个共享接口
使用该接口标记所有处理器服务
为每个处理器定义一个getDefaultIndexName()
在需要使用此服务的类中插入带标记的服务定位器
您可以让所有服务自动连接
注意:您可以使用抽象类而不是接口,它的工作方式也一样。我更喜欢使用界面,但这取决于你
为完成起见,上述内容适用于Symfony 4.3。不是肯定的,但可能是ProcessServiceLocator,而不是process factory。基本上,流程定位器将包含可用流程服务的列表。Symfony auto-wiring将我作为参数输入的任何内容注入构造函数。我想在这里实现同样的目标。当我向处理器类的构造函数添加参数时,我希望注入此服务,否则就不需要。然后我仍然会从工厂中注入实体经理,但我认为这与解决方案不相关。连接服务定位器似乎起作用了。我在数据库表中有一个带有文件前缀的间接寻址,用于确定哪个处理器处理文件,因此我使用strval($type->getId())与公共静态函数getDefaultIndexName()匹配。但是,服务定位器找不到处理器,当我调试时,我看不到getDefaultIndexName()返回值在任何地方都是如何索引的。不,它不起作用。我只能通过处理器的完全限定类名来获取处理器。还将composer更新为最新的4.3版本,并尝试将getDefaultIndexName置于不带引号的位置,如下所示:代码中肯定存在其他类型的错误。我在SF4.3和SF5上测试了上述内容。它起作用了。我已经设置好,你可以看到它的行动。只需克隆它,进行composer安装
,然后运行bin/console foobar
,它就会工作。这里只有几个类,所以没有什么神秘之处。附录:getDefaultIndexName中指定的方法需要返回一个字符串,而strval(number)似乎还不够。如果返回的键不是字符串,则标记的服务将按查找顺序或完整类名进行索引,并且可能不正确匹配,或者调用serviceLocator->get将返回错误。您也是一个命中PHP限制。在关联数组中使用数字字符串作为键既不安全也不明智。例如,见。