本文介绍了如何修复 Symfony 5 包中定义的控制器上的“...SomeController has no container set"?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我创建了一个自定义的 Symfony 5.3 包来在不同的项目之间共享代码.在 src/controller/SomeController.php 中,bundle 实现了一个控制器类,它从 Symfony\Bundle\FrameworkBundle\Controller\AbstractController 扩展而来.

I have created a custom Symfony 5.3 bundle to share code between different projects. In src/controller/SomeController.php the bundle implements a controller class which extends from Symfony\Bundle\FrameworkBundle\Controller\AbstractController.

通过我的 Symfony 项目中的路由访问此控制器时,出现以下错误:

When accessing this controller via a route in my Symfony project I get the following error:

XY\CommensBundle\Controller\SomeController"没有设置容器,做了您忘记将其定义为服务订阅者了吗?

AbstractController 有一个 setContainer 方法,用于注入服务容器.在我的 Symfony 项目中直接实现的控制器上,此方法由 autowire/autoconfigure 自动调用.

AbstractController has a setContainer method which is used to inject the service container. On controllers implemented directly in my Symfony project this method is called automatically by autowire / autoconfigure.

然而,关于 Symfony 文档 autowire/autoconfigure 应该不可用于捆绑服务.相反,应明确定义所有服务.所以我将它添加到包 services.yaml:

However, regarding to the Symfony docs autowire / autoconfigure should not be used for bundle services. Instead, all services should be defined explicitly. So I added this to the bundles services.yaml:

# config/services.yaml
services:    
    xy_commons.controller.some_controller:
        class: XY\CommensBundle\Controller\SomeController
        public: false
        calls:     
            - [ setContainer, [ '@service_container' ]]

在使用 Composer 将捆绑包添加到我的 Symfony 项目后,控制台显示控制器已正确添加为服务.一切看起来都很好.

After adding the bundle to my Symfony project using Composer the console shows, that the controller is correctly added as a service. Everything seems fine.

php bin/console debug:container 'xy_commons.controller.some_controller'

Information for Service "xy_commons.controller.some_controller"
=============================================================

 ---------------- ------------------------------------------------------- 
  Option           Value                                                  
 ---------------- ------------------------------------------------------- 
  Service ID       xy_commons.controller.some_controller                    
  Class            XY\CommensBundle\Controller\SomeController 
  Tags             -                                                      
  Calls            setContainer                                           
  Public           no                                                     
  Synthetic        no                                                     
  Lazy             no                                                     
  Shared           yes                                                    
  Abstract         no                                                     
  Autowired        no                                                     
  Autoconfigured   no                                                     
 ---------------- ------------------------------------------------------- 

但是,错误还是一样.那么如何正确配置Bundles中的控制器/服务?

SomeController 只是 AbstractController 的一个非常基本的子类:

SomeController is just a very basic subclass of AbstractController:

class SomeController extends AbstractController {
    public function some(): Response {
        return new Response("<html><body>OK</body></html>");
    }
}

完全限定的类名作为服务 ID

通常我使用 FQCN 作为服务 ID.但是,在这种情况下,我遵循了 Symfony 文档(上面链接)的建议,其中明确表示不要这样做:

Usually I use FQCNs as Service ID. However, in this case I followed the advise from the Symfony docs (linked above) which explicitly say not to do so:

如果bundle定义了服务,它们必须以bundle为前缀别名而不是像你在你的项目服务.例如,AcmeBlogBu​​ndle 服务必须是以 acme_blog 为前缀.原因是 bundles 不应该依赖不强加服务自动装配或自动配置等功能编译应用服务时的开销.

此外,不打算由应用程序使用的服务直接,应该定义为私有.

In addition, services not meant to be used by the applicationdirectly, should be defined as private.

这是我使用蛇名作为 ID 并将服务设为私有的方式.控制器并没有真正用作服务,而只是在通过路由访问/调用它时由 Symfony 自动创建.

This is way I used a snake name as ID and made the service private. The controller is not really used as service but only created automatically by Symfony when accessing / calling it via a route.

推荐答案

所以@ArturDoruch 有正确的要点.您应该使用完全限定的类名作为服务 ID,但我想您可以使用蛇形大小写,只要您也在路由文件中使用它.但是没有特别的理由不使用类名.

So @ArturDoruch has the correct main points. You should use the fully qualified class name as the service id though I suppose you could use snake case as long as you also used it in your routes file. But there is no particular reason not to use the class name.

您的控制器服务也需要公开,以便控制器解析器可以从 DI 容器中提取它.如果服务不是公开的,那么解析器只会尝试新建控制器,并且永远不会调用 set container 或注入任何构造函数参数.这就是为什么您会收到有关未设置容器的错误的原因.顺便说一句,使用 controller.service_arguments 标记服务的一个神奇的副作用是该服务变得公开.

Your controller service also needs to be public so the controller resolver can pull it from the DI container. If the service is not public then the resolver just tries to new the controller and will never call set container or inject any constructor args. That is why you get the error about no container set. By the way, a magical side effect of tagging the service with controller.service_arguments is that the service becomes public.

问题是,在捆绑包中使用控制器不再是你经常看到的东西,主要是因为如果应用程序想要稍微调整控制器的工作方式会很痛苦.

The thing is that using controllers in bundles is just not something you see much anymore mostly because it's a pain if application want to slightly tweak the way controller works.

因此,如果您查看最佳实践,您会发现捆绑控制器不应扩展 AbstractConroller,捆绑不应使用自动装配.对于大多数捆绑包来说都是很好的建议,但如果你只是想让东西工作,那么很容易陷入困境.

So if you look in the best practices you see things like bundle controllers should not extend AbstractConroller and bundles should not use autowire. Good advice for most bundles but if you just want to get stuff working then it's easy to get bogged down.

我建议从应用程序附带的相同 services.yaml 文件开始.您需要稍微调整路径:

I would suggest starting with the same services.yaml file that comes with the application. You need to tweak the paths slightly:

# Resources/config/services.yaml
services:

    _defaults:
        autowire: true
        autoconfigure: true

    MyBundle\:
        resource: '../../'
        exclude:
            - '../../Resources/'
            - '../../DependencyInjection/'
            - '../../Entity/'

# bin/console debug:container MyController
  Service ID       MyBundle\Controller\MyController  
  Class            MyBundle\Controller\MyController  
  Tags             controller.service_arguments      
                   container.service_subscriber      
  Calls            setContainer                      
  Public           yes                               
  Synthetic        no                                
  Lazy             no                                
  Shared           yes                               
  Abstract         no                                
  Autowired        yes                               
  Autoconfigured   yes       

现在您可以专注于让您的捆绑包工作.让 autowire 做繁重的工作.一旦捆绑包稳定下来,也许您可​​以返回并开始按照最佳实践中的建议手动定义您的服务.或者也许不是.

Now you can focus on just getting your bundle to work. Let autowire do the heavy lifting. Once the bundle has stabilized then maybe you can go back in and start to manually define your services as recommended in the best practices. Or maybe not.

这篇关于如何修复 Symfony 5 包中定义的控制器上的“...SomeController has no container set"?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

10-30 17:09