为了账号安全,请及时绑定邮箱和手机立即绑定

如何在 Symfony 5 的其他目录中自动注册“控制器即服务”

如何在 Symfony 5 的其他目录中自动注册“控制器即服务”

PHP
红糖糍粑 2023-04-21 17:26:59
背景我想使用功能内聚来组织我的控制器。这意味着我不会有一个Controllers/目录,这使得框架很容易,但我会按用例组织我的代码。一个任意的例子:src/FetchLatestNews/Controller.phpsrc/FetchLatestNews/News.phpsrc/FetchLatestNews/NewsRepository.php...ETC但是,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 中实现此工作流程?应该是,因为路由中的任何内容 --> 某个类都将是控制器。有没有办法使用正则表达式或其他解决方案?注意:这个答案暗示了一个空的界面类型的“hack”。这也不理想。还有其他选择吗?
查看完整描述

1 回答

?
蝴蝶刀刀

TA贡献1801条经验 获得超8个赞

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

标签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以允许 setter 注入(如果您不关心 setter 注入,可以忽略它)

  • 将类设置为公共的,因为 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 标签,它解析构造函数参数并检查一些特征。如果你让你的编译器传递优先级更高,那么你可能很容易解决这个问题......


查看完整回答
反对 回复 2023-04-21
  • 1 回答
  • 0 关注
  • 72 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信