我想使用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中实现此工作流程?应该是,因为来自路由的任何内容->某些类都将是控制器。有没有办法使用正则表达式或其他解决方案?
Note:this answer建议使用一个空接口,称为“ hack”。这也不理想。有其他选择吗?
请注意,此答案已使用新信息再次更新。
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
中手动添加自己的标签才能允许二传手注入(如果您不关心二传手注入,则可以忽略它)别忘了您不必为“名称中的控制器”使用逻辑。可以在类名的末尾添加“ 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标记,该标记解析构造函数参数并检查某些特征。如果您提高编译器传递的优先级,则可以轻松解决此问题,然后...