jdo*_*dog 5 php dependency-injection symfony symfony4 symfony-dependency-injection
我有一个 Sumfony 4.3 命令,可以处理一些数据并循环通过多个“处理器”来进行处理。该代码使用工厂(自动装配),然后实例化该命令。
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);
}
Run Code Online (Sandbox Code Playgroud)
但我仍然必须:
我有大约 70 个 Processor 子类。他们都使用EntityInterface,但只有少数使用SwiftMailer和其他依赖项。
由于我要添加仅由少数处理器使用的服务,因此我正在寻找一种仅在处理器级别自动装配这些参数的方法。理想情况下,也无需将服务定义添加到 services.yml
总之,我希望能够向 的任何子类添加依赖项Processor,即使它是其他子类的父类并且自动注入依赖项。
在您的代码中,有很多内容并不是立即显而易见的,但解决此问题的典型方法是使用“服务定位器”。文档。
假设您有多个实现该接口的服务Processor:
界面:
interface Processor {
public function process($file): void;
}
Run Code Online (Sandbox Code Playgroud)
耦合实现:
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';
}
}
Run Code Online (Sandbox Code Playgroud)
耦合实现:
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';
}
}
Run Code Online (Sandbox Code Playgroud)
请注意,每个处理器都有完全不同的依赖关系,并且可以直接自动连接,并且每个处理器都有一个getDefaultIndexName()方法。
现在我们将“标记”所有实现该Processor接口的服务:
interface Processor {
public function process($file): void;
}
Run Code Online (Sandbox Code Playgroud)
这里注意:文档说,如果您定义了 a,public static function getDefaultIndexName()则默认情况下会选择它。但我发现这目前不起作用。但如果您定义了 ,default_index_method则可以将其连接到您选择的方法。getDefaultIndexName我暂时保留了,但你可以选择你自己的选择。
现在,如果您需要在控制台命令中执行此过程,例如:
use Symfony\Component\DependencyInjection\ServiceLocator;
class MyConsoleCommand
{
private ServiceLocator $locator;
public function __construct(ServiceLocator $locator)
{
$this->locator = $locator;
}
}
Run Code Online (Sandbox Code Playgroud)
要注入服务定位器,您可以执行以下操作:
#services.yaml
services:
App\HandlerCollection:
arguments: [!tagged_locator { tag: 'processor_services' } ]
Run Code Online (Sandbox Code Playgroud)
要从服务定位器获取任何处理器,您可以执行以下操作:
$fooProcessor = $this->locator->get('candidateFileOne');
$barProcessor = $this->locator->get('candidateFileTwo');
Run Code Online (Sandbox Code Playgroud)
总结一下,基本上你需要的是:
getDefaultIndexName(),这可以帮助您将文件与处理器匹配。您可以让所有服务自动连接。
注意:您可以使用抽象类而不是接口,并且它的工作方式相同。我更喜欢使用界面,但这取决于你。
为了完整起见,这里是一个适用于 Symfony 4.3 的存储库。
| 归档时间: |
|
| 查看次数: |
868 次 |
| 最近记录: |