我如何在Symfony 5的其他目录中自动注册'控制器作为服务'

问题描述 投票:2回答:1

背景

我想使用functional cohesion来组织我的控制器。

这意味着我将没有Controllers/目录,这使该框架变得容易,但是我将按用例组织代码。任意示例:

  • src/FetchLatestNews/Controller.php
  • src/FetchLatestNews/News.php
  • src/FetchLatestNews/NewsRepository.php
  • ...等

但是,默认情况下,Symfony设置为将所有控制器都放在一个位置。参见services.yaml

    # controllers are imported separately to make sure services can be injected
    # as action arguments even if you don't extend any base controller class
    App\Controller\:
        resource: '../src/Controller'
        tags: ['controller.service_arguments']

按照上述模板,我可以只是将新的src/FetchLatestNews/Controller放在其中,然后每次添加每个新的控制器。但是,我不想,也不必每次创建新用例时都手动更新此文件

问题

如何避免此错误:

也许您忘记了将控制器注册为服务,或者错过了使用“ controller.service_arguments”对其进行标记的时间

作为用户,我不必在意手动添加service_arguments或任何其他配置。当我指定一条路线时,它指向的对象就是控制器。

理想的工作流程是:

  • 在我喜欢的地方创建一个控制器。
  • 向引用控制器的routes.yaml添加路由。
  • 就是这样!

是否可以在Symfony中实现此工作流程?应该是,因为来自路由的任何内容->某些类都将是控制器。有没有办法使用正则表达式或其他解决方案?

Notethis answer建议使用一个空接口,称为“ hack”。这也不理想。有其他选择吗?

php symfony symfony4 symfony5
1个回答
0
投票

请注意,此答案已使用新信息再次更新

controller.service_arguments标签仅用于允许动作注入。坚持使用构造函数注入和__invoke(),这就是您所需要的。

以下是公共添加,但事实证明,您不需要为此提供编译器通道,并且可以在services.yaml级别上完成所有操作。因此,您唯一需要的是以下内容:

services:
    _defaults:
        autowire: true
        autoconfigure: true
        public: true         // <---- This is the new additional config.

然后,任何类都可以在routes.yaml中用作控制器,并且构造注入可以正常工作。

注意:原始答案如下。感谢Jakumi为我指出正确的方向。

您需要添加编译器通道才能完成两件事。在引导和注册用于自动依赖项注入的东西时,如果类的名称中包含Controller

  • 添加标签controller.service_arguments,这是通常需要在services.yaml中手动添加自己的标签才能允许二传手注入(如果您不关心二传手注入,则可以忽略它)
  • 将类设置为公共,因为Symfony有一个奇怪的想法,即不应从类访问修饰符中推断出公共或私有任何概念(严重吗?)-这很重要,因为Symfony会抱怨说,控制器是私有的,否则

别忘了您不必为“名称中的控制器”使用逻辑。可以在类名的末尾添加“ Controller”,这可能会更好。

无论如何,首先创建一个CompilerPass。将其放在您想要的任何地方。我把我的放在Kernel.php旁边。

use Symfony\Component\DependencyInjection\{Compiler\CompilerPassInterface, ContainerBuilder};

class ControllersAsServices implements CompilerPassInterface
{
    public function process(ContainerBuilder $container)
    {
        foreach ($container->getDefinitions() as $definition) {
            if (strpos($definition->getClass(), "Controller") === false) {
                continue;
            }

            $definition->addTag("controller.service_arguments");
            $definition->setPublic(true);
        }
    }
}

然后在Kernel.php中,您需要告诉它使用此新名称。如文档所示,重写受保护的函数build并添加您的编译器传递:

protected function build(ContainerBuilder $container)
{
    $container->addCompilerPass(new ControllersAsServices, PassConfig::TYPE_BEFORE_OPTIMIZATION, -1);

    parent::build($container);
}

清除开发人员缓存。在执行此操作之前,我什么都没有改变。

现在,名称中带有Controller的任何类都可以自动连接,因为它本来应该是原来的。

下面来自Jakumi的原始分析器:


我相信您的最佳方法是实现CompilerPass,向容器中添加类:

https://symfony.com/doc/current/bundles/extension.html#adding-classes-to-compile

在此过程中,可能还可以添加标签。

Symfony通过RegisterControllerArgumentLocatorsPass迎合controller.service_arguments标记,该标记解析构造函数参数并检查某些特征。如果您提高编译器传递的优先级,则可以轻松解决此问题,然后...

© www.soinside.com 2019 - 2024. All rights reserved.