繁体   English   中英

将服务名称传递给 Symfony 中的服务工厂 5

[英]Passing service name to service factory in Symfony 5

我正在将 Symfony 集成到具有自己基于 PSR-11 的依赖容器的旧应用程序中。 一直在寻找一种将 DI 容器合并到 Symfony 使用的解决方案,但一无所获。 为了让它发挥作用,我提出了一个我不喜欢的“hacky”解决方案。

我已经创建了这个 class。 它在其中创建一个旧 DI 容器的实例:

class OldAppServiceFactory
{
    private ContainerInterface $container;

    public function __construct()
    {
        $this->container = OldContainerFactory::create();
    }

    public function factory(string $className)
    {
        return $this->container->get($className);
    }
}

并在services.yaml中添加了正确的条目:

    oldapp.service_factory:
        class: Next\Service\LeonContainer\LeonServiceFactory

    OldApp\Repository\Repository1:
        factory: ['@oldapp.service_factory', 'factory']
        arguments:
            - 'OldApp\Repository\Repository1'

    OldApp\Repository\Repository2:
        factory: ['@oldapp.service_factory', 'factory']
        arguments:
            - 'OldApp\Repository\Repository2'

    OldApp\configuration\ConfigurationProviderInterface:
        factory: ['@oldapp.service_factory', 'factory']
        arguments:
            - 'OldApp\configuration\ConfigurationProviderInterface'

通过上述技巧,将这些类放入服务 class 构造函数中是可行的。 不幸的是,它看起来很糟糕,用更多的存储库来扩展它会很痛苦(尤其是当有 50 个时)。 是否可以在services.yaml中实现类似的功能?

    OldApp\Repository\:
        factory: ['@oldapp.service_factory', 'factory']
        arguments:
            - << PASS FQCN HERE >>

这将使我在services.yaml中只有一个条目,用于旧应用程序的单个命名空间。

但是,也许我的问题还有其他解决方案? 一直在尝试配置Kernel.phpprepareContainer(...)方法,但我也没有任何结果,因为旧的依赖项在一个 PHP 文件中返回一个数组:

return array [
    RepositoryMetadataCache::class => static fn () => RepositoryMetadataCache::createFromCacheFile(),
    EntityCollection::class => autowire(EntityCollection::class),
    'Model\Repository\*' => static function (ContainerInterface $container, RequestedEntry $entry) { ... }
];

您可以使用自定义编译器传递轻松完成此操作。

首先通过加载它们所在的目录来标记所有旧的存储库类:

OldApp\Repository\:
    resource: '../src/OldApp/Repository/*'
    autowire: false
    autoconfigure: false
    tags: ['oldapp_repository']

(我认为您可能还需要将src/OldApp从默认的自动服务加载中排除。例如:

App\:
    resource: '../src/*'
    exclude: '../src/{OldApp/Repository,DependencyInjection,Entity,Tests,Kernel.php}'

...但我不是 100% 确定,测试这个)。

然后通过标签创建一个编译器传递给 go 并为每个标签定义一个工厂:

class OldAppRepositoryCompilerPass implements CompilerPassInterface
{

    public function process(ContainerBuilder $container): void
    {
        $taggedServices = $container->findTaggedServiceIds('oldapp_repository');

        foreach ($taggedServices as $serviceId => $tags) {

            $definition = $container->getDefinition($serviceId);
            $definition
                    ->setFactory([service('@oldapp.service_factory'), 'factory'])
                    ->addArgument($serviceId);
        }

    }
}

在您的应用程序 Kernel build()方法中添加编译器通道:

// src/Kernel.php
namespace App;

use Symfony\Component\HttpKernel\Kernel as BaseKernel;
// ...

class Kernel extends BaseKernel
{
    // ...

    protected function build(ContainerBuilder $container): void
    {
        $container->addCompilerPass(new OldAppRepositoryCompilerPass());
    }
}

目前无法对此进行正确测试,但这应该会让您朝着正确的方向前进。 有关更多详细信息,请查看文档:

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM