简体   繁体   English

$ this-> container在Symfony3上的Controller中为NULL

[英]$this->container is NULL in Controller on Symfony3

I got an annoying problem when I call this in a controller (ClientDomainController) : 当我在控制器(ClientDomainController)中调用它时,我遇到了一个烦人的问题:

    $this->getDoctrine()->getManager();

I got this error : 我收到此错误:

    Call to a member function has() on null

I looked the stack trace and see that : 我查看了堆栈跟踪,发现:

    $this->container is null

My controller extends from the Symfony Controller component : 我的控制器来自Symfony Controller组件:

    use Symfony\Bundle\FrameworkBundle\Controller\Controller;

The funny thing is that in an other controller (HomeController) I make the exact same things : 有趣的是,在另一个控制器(HomeController)中,我做了完全一样的事情:

  1. Extend from Controller (the exact same class) 从Controller扩展(完全相同的类)
  2. Get Doctrine 获取学说
  3. Get the EntityManager 获取EntityManager
  4. Use the manager 使用经理

And this without any error. 而且这没有任何错误。

The only one difference between the HomeController and the ClientDomainController is that the second one is a service. HomeController和ClientDomainController之间的唯一区别是第二个是服务。 So I wrote it in the services.yml file : 所以我把它写在services.yml文件中:

    services:
        client_domain:
            class: AppBundle\Controller\ClientDomainController

Finally I tested many things like creating a constructor to my controller and adding this to the services.yml file (things that a never did to the functional one) : 最后,我测试了很多事情,例如在控制器中创建一个构造函数,并将其添加到services.yml文件中(从不对功能函数执行此操作):

    arguments: [ 'doctrine.orm.entity_manager' ]

When you register your controller as a service, then, Symfony creates it just like you tell it to do. 然后,当您将控制器注册为服务时,Symfony会像您说的那样创建它。

So the difference is that although your controller implements ContainerAwareInterface (via extending Controller class), in the end nobody calls setContainer method to utilize this interface and set $container 's value. 因此区别在于,尽管您的控制器实现了ContainerAwareInterface (通过扩展Controller类),但最终没有人调用setContainer方法来利用此接口并设置$container的值。 You have to do it manually in you services.yml config like: 您必须在services.yml配置中手动执行此操作,例如:

    calls:
        - [ setContainer, [ @service_container ] ]

But this is not the best solution 但这不是最好的解决方案

Registering your controllers as services is good in general. 将您的控制器注册为服务通常是好的。 It makes them more testable and maintainable. 它使它们更具可测试性和可维护性。

But this is true as long as you stick to the good rules of OOP . 但这是正确的,只要您遵守OOP的良好规则即可 In this case when you pass whole container it means that: 在这种情况下,当您传递整个容器时,这意味着:

  1. Your controller instance may have an invalid state if you do not pass container (or you should handle that it may not be set everywhere you use it), which is bad by design. 如果您不传递容器,则控制器实例的状态可能无效(或者应该处理它可能不会在使用它的任何地方设置),这在设计上是很糟糕的。
  2. It's hard to test, since you have to mock whole container instead of only dependences that this controller uses. 很难测试,因为您必须模拟整个容器,而不仅仅是模拟该控制器使用的依赖项。
  3. Dependences are not explicitly defined as you need to look into controller's code in order to know what are the dependences fetched form container. 没有明确定义依赖关系,因为您需要查看控制器的代码才能知道从容器中获取的依赖关系是什么。

In short words, dependences should be passed via contrustor as you did in the end or you could use action-based dependency injection when a dependency is used only in this particular action. 简而言之,应该像最后一样通过依赖者传递依赖关系,或者当仅在此特定操作中使用依赖关系时,可以使用基于操作的依赖关系注入

Actually best solution would be even not to extend base Controller class to make you controllers framework independent. 实际上,最好的解决方案甚至是不扩展基础Controller类以使您的控制器框架独立。

Your controller should be like this: 您的控制器应如下所示:

class HelperController extends Controller
{

    /**
     * @var \Symfony\Component\DependencyInjection\ContainerInterface
     */
    protected $container;

    /**
     * HelperController constructor.
     *
     * @param ContainerInterface $container
     */
    public function __construct(ContainerInterface $container)
    {
        $this->container = $container;
    }
  }

and in config file (in my case xml) 并在配置文件中(在我的情况下为xml)

    <service id="xxx.helper_controller" class="xxx\EspacePROBundle\Controller\HelperController">
        <argument type="service" id="service_container" />
    </service>

I hope it help. 希望对您有所帮助。

As stated in Jakub Matczak's answer the DI container for the controller is not set. 如Jakub Matczak的回答所述,未设置控制器的DI容器。

Alternatively to adding the calls section in your services.yml you can call setContainer in the constructer of the class where you want to use the controller. 除了在services.yml中添加calls部分之外,您还可以在要使用控制器的类的构造函数中调用setContainer

use App\Controller\MyController;
use Symfony\Component\DependencyInjection\ContainerInterface;

class MyClass {

    /**
     * The injected controller
     * @var MyController
     */
    protected $my_controller;

    public function __construct(MyController $my_controller, ContainerInterface $container) {
        $this->my_controller = $my_controller;
        $this->my_controller->setContainer($container);
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM