Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/php/294.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Php 在子类中循环时如何使用自动连接?_Php_Symfony_Dependency Injection_Symfony4_Symfony Dependency Injection - Fatal编程技术网

Php 在子类中循环时如何使用自动连接?

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

我有一个sumfony4.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);
                    }
然而,我仍然必须:

  • 自动连接工厂和处理器顶级中的所有参数
  • 将所有参数按正确顺序传递给处理器
我有大约70个子类的处理器。它们都使用
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限制。在关联数组中使用数字字符串作为键既不安全也不明智。例如,见。