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

$this->container is NULL in Controller on Symfony3

当我在控制器 (ClientDomainController) 中调用它时遇到了一个恼人的问题:

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

我收到这个错误:

    Call to a member function has() on null

我查看了堆栈跟踪并看到:

    $this->container is null

我的控制器扩展自 Symfony 控制器组件:

    use Symfony\Bundle\FrameworkBundle\Controller\Controller;

有趣的是,在另一个控制器 (HomeController) 中,我做了完全相同的事情:

  1. 从控制器扩展(完全相同class)
  2. 获取学说
  3. 获取实体管理器
  4. 使用管理器

而且这没有任何错误。

HomeController 和ClientDomainController 之间的唯一区别是第二个是服务。所以我把它写在 services.yml 文件中:

    services:
        client_domain:
            class: AppBundle\Controller\ClientDomainController

最后我测试了很多东西,比如为我的控制器创建一个构造函数并将其添加到 services.yml 文件(功能性文件从未做过的事情):

    arguments: [ 'doctrine.orm.entity_manager' ]

你的控制器应该是这样的:

class HelperController extends Controller
{

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

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

在配置文件中(在我的例子中 xml)

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

希望对您有所帮助。

当您将控制器注册为服务时,Symfony 会按照您告诉它的方式创建它。

所以不同之处在于,尽管您的控制器实现了 ContainerAwareInterface(通过扩展 Controller class),但最终没有人调用 setContainer 方法来利用此接口,并且设置 $container 的值。您必须在 services.yml 配置中手动执行此操作,例如:

    calls:
        - [ setContainer, [ @service_container ] ]

但这不是最好的解决方案

将您的控制器注册为服务通常是好的。它使它们更易于测试和维护。

但只要您坚持 OOP 的良好规则,这就是事实。 在这种情况下,当您传递整个容器时,这意味着:

  1. 如果你不传递容器,你的控制器实例可能有一个无效状态(或者你应该处理它可能不会在你使用它的任何地方设置),这在设计上是糟糕的。
  2. 很难测试,因为您必须模拟整个容器,而不仅仅是该控制器使用的依赖项。
  3. 依赖关系未明确定义,因为您需要查看控制器的代码才能了解从容器中获取的依赖关系。

简而言之,依赖项应该像您最后所做的那样通过 contrustor 传递,或者当依赖项仅用于此特定操作时,您可以使用 action-based dependency injection

实际上最好的解决方案甚至是不扩展基础 Controller class 以使您的控制器框架独立。

如 Jakub Matczak 的回答所述,控制器的 DI 容器未设置。

或者在 services.yml 中添加 calls 部分,您可以在要使用控制器的 class 的构造函数中调用 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);
    }
}