php - 如何在 Symfony 5 的其他目录中自动注册 'controllers as services'

标签 php symfony symfony4 symfony5

背景

我想使用functional cohesion组织我的 Controller 。

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

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

但是,Symfony 默认设置为要求所有 Controller 位于一处。请参阅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 。但是,我不想、也不应该在每次创建新用例时手动更新此文件

问题

如何避免此错误:

maybe you forgot to register the controller as a service or missed tagging it with the "controller.service_arguments"

作为用户,我不必关心手动添加 service_arguments 或任何其他配置。当我指定一条路线时,它所指向的东西就是一个 Controller 。

理想的工作流程是:

  • 在我喜欢的任何地方创建一个 Controller 。
  • 将路由添加到引用 Controller 的 routes.yaml 中。
  • 就是这样!

是否可以在 Symfony 中实现这个工作流程?应该是这样,因为路由中的任何东西 --> 某个类都将是 Controller 。有没有办法使用正则表达式或其他解决方案?

注意:this answer建议使用一种空接口(interface)“hack”。这也不理想。还有其他选择吗?

最佳答案

Please note, this answer has been updated again with new information.

仅在允许操作注入(inject)时才需要 controller.service_arguments 标记。坚持使用构造函数注入(inject)和 __invoke() 这就是您所需要的。

下面的公共(public)添加是一个要求,但事实证明您不需要为此提供编译器通行证,并且这一切都可以在 services.yaml 级别完成。因此,您唯一需要的是以下内容:

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

然后,任何类都可以在 routes.yaml 中用作 Controller ,并且构造注入(inject)工作得很好。

Note: The original answer is below. Thanks to Jakumi for pointing me in the right direction.

您需要添加编译器 channel 来完成两件事。在启动和注册自动依赖项注入(inject)时,如果类的名称中包含 Controller:

  • 添加标记 controller.service_arguments,这是您通常需要在 services.yaml 中手动添加的标记,以允许 setter 注入(inject)(您可以忽略此标记)如果你不关心 setter 注入(inject))
  • 将类设置为 public,因为 Symfony 有一个奇怪的想法,即公共(public)或私有(private)任何东西的概念都不能从类访问修饰符中推断出来(认真的吗?) - 这是 Symfony 会提示的重要事情否则 Controller 是私有(private)的

不要忘记您不必使用“名称中的 Controller ”的逻辑。可以将“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 中,您需要告诉它使用这个新的。覆盖 protected 函数 build 并添加编译器传递,如文档所示:

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

    parent::build($container);
}

清除您的开发缓存。在我这样做之前,一切都没有改变。

现在,任何名称中带有 Controller 的类都可以像原来那样 Autowiring 。

Jakumi 的原始答案如下:


我相信您的情况最好的方法是实现 CompilerPass,将类添加到容器中:

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

在此过程中,可能还有一种方法可以添加标签。

Symfony 通过RegisterControllerArgumentLocatorsPass 来满足controller.service_arguments 标签的需求。 ,它解析构造函数参数并检查一些特征。如果你的编译器通行证的优先级更高,你可能可以轻松解决这个问题......

关于php - 如何在 Symfony 5 的其他目录中自动注册 'controllers as services',我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62115738/

相关文章:

php - 使用angularjs在表格中显示由PHP形成的json数据

symfony - 我们如何在 Twig View 中获取实体对象的类名

php排序数组的三个维度

validation - Symfony2 验证 - 没有为约束配置默认选项

symfony - 当referencedColumnName与 "id"不同时,在Symfony2中使用解析目标实体的实体之间的关系

php - 如何在 Symfony4 结构的参数文件中检索我的环境变量?

Symfony 表单 : Uploaded file - "This value should be of type string"

php - 没有传输支持给定的 Messenger DSN

php - foreach SMARTY 中的下一项

php - 将图像从 PHP GD 图像对象存储到数据库的有效方法