【问题标题】:Symfony3 controller constructor injection is not workingSymfony3 控制器构造函数注入不起作用
【发布时间】:2017-01-21 12:55:20
【问题描述】:

我想使用以下代码将 EntityManager 实例传递给我的控制器的构造函数:

namespace AppBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Doctrine\ORM\EntityManager;

class UserController extends Controller
{

    public function __construct( EntityManager $entityManager )
    {
        // do some stuff with the entityManager
    }
}

我通过将参数放入 service.yml 文件来进行构造函数注入:

parameters:
#    parameter_name: value

services:
#    service_name:
#        class: AppBundle\Directory\ClassName
#        arguments: ["@another_service_name", "plain_value", "%parameter_name%"]
    app.user_controller:
        class: AppBundle\Controller\UserController
        arguments: ['@doctrine.orm.entity_manager']

service.yml 包含在 config.yml 中,当我运行时

php bin/console debug:container app.user_controller

我明白了:

 Information for Service "app.user_controller"
 =============================================

 ------------------ ------------------------------------- 
  Option             Value                                
 ------------------ ------------------------------------- 
  Service ID         app.user_controller                  
  Class              AppBundle\Controller\UserController  
  Tags               -                                    
  Public             yes                                  
  Synthetic          no                                   
  Lazy               no                                   
  Shared             yes                                  
  Abstract           no                                   
  Autowired          no                                   
  Autowiring Types   -                                    
 ------------------ ------------------------------------- 

但是,调用映射到我的控制器的路由,我得到:

UserController.php 第 17 行中的 FatalThrowableError:类型错误: 参数 1 传递给 AppBundle\Controller\UserController::__construct() 必须是一个实例 Doctrine\ORM\EntityManager 的,没有给出,调用 /home/michel/Documents/Terminfinder/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Controller/ControllerResolver.php 在第 202 行

我不明白,为什么 EntityManager 没有被注入?

【问题讨论】:

  • 你清除缓存了吗?
  • @lxg 我使用 php bin/console cache:clear --env=dev 清除了缓存并手动删除了 var/cache/dev/*
  • 将控制器声明为服务并不被 fabpot 自己认为是一种好的做法:github.com/symfony/symfony-docs/issues/457。我同意这一点。
  • @COil 我明白你的意思,但是有一篇关于如何将控制器用作服务的官方文档文章。 symfony.com/doc/current/controller/service.html
  • 您需要配置路由以将控制器用作服务:symfony.com/doc/current/controller/… 并忽略反对者。将控制器定义为服务非常好。如果您仍然遇到问题,请使用您的路线更新问题。

标签: php symfony


【解决方案1】:

使用基类Controller.php 时,Container 通常由ControllerResolver 中的框架自动连接。

基本上,您是在试图混淆事物的实际运作方式。

要解决您的问题,您基本上有两种解决方案:

  1. 不要尝试注入依赖项,而是直接从您的操作/方法中的容器中获取它。

public function listUsers(Request $request) { $em = $this->container->get('doctrine.orm.entity_manager'); }

  1. 手动创建控制器,但不扩展控制器基类;和set ip up as a service

在这一点上更进一步,有些人会建议不要使用 Symfony 提供的默认控制器。

虽然我完全理解他们的观点,但我在这个主题上稍微温和一些。

仅注入所需依赖项背后的想法是避免并迫使人们拥有瘦控制器,这一件好事。

但是,通过一点点自动确定,使用现有的快捷方式要简单得多。

Controller / Action 只不过是您的Views 和您的Domain/Models 之间的粘合剂。

使用ContainerAware 工具防止自己在Controller 中做太多事情。

Controller 可以丢弃而不会在您的系统中产生业务更改。

【讨论】:

  • 感谢您的回答!让我感到困惑的是,在配置了额外的路由时,您不能从 Controller 基类继承。我最终决定编写服务来完成我的整个业务逻辑,包括使用 entityManager,并将控制器类用作粘合组件(但保留从控制器基类的继承)。
  • 谢谢,但我可以知道你从哪里得到doctrine.orm.entity_manager 吗?如果需要配置,我该怎么做?
【解决方案2】:

自 2017 年和 Symfony 3.3+ 起,原生支持控制器即服务。

您可以保持控制器保持原样,因为您正确使用了构造函数注入

只需修改您的services.yml

# app/config/services.yml

services:
    _defaults:
        autowire: true

    AppBundle\:
        resouces: ../../src/AppBundle

它会:

  • 将所有控制器和存储库作为服务加载
  • autowire 构造函数依赖项(在您的情况下为 EntityManager


更进一步:存储库即服务

关于 Doctrine + 存储库 + 服务 + 控制器 有很多关于 SO 的问题,所以我已经放下了一个 general answer to a post。明确检查您是否更喜欢构造函数注入和服务而不是静态和服务定位器。

【讨论】:

    【解决方案3】:

    您是否使用以下模式调用控制器AppBundle:Default:index?如果是,那应该是问题所在。如果要将控制器用作服务,则必须使用模式:app.controller_id:indexAction,它使用服务的 id 来加载控制器。

    否则它将尝试在不使用服务容器的情况下创建类的实例。

    有关更多信息,请参阅有关此主题的 symfony 文档https://symfony.com/doc/current/controller/service.html

    【讨论】:

      【解决方案4】:

      实体管理器在控制器中可用,无需注入它。只需要:

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

      【讨论】:

      • 不回答问题。相应地投了反对票。
      • 因此,即使问题涉及实现某些预期结果的不适当手段,答案也应该解决问题?这样做似乎会强化不良做法。
      • 评论适用于“不要那样做”之类的东西。或提供答案,然后添加警告。或者,如果您真的认为这是一种不好的做法,请为您的立场提供一些支持。但我想你可能会发现很难让许多开发人员相信服务定位器是“好的”而依赖注入是“坏的”。
      • 但是在这种情况下,这个控制器扩展了默认的 Symfony 控制器。那么为什么要注入 EM,因为基础 Symfony 控制器已经提供了它。
      • @COil - 很好的问题,虽然它并不真正适用于这类论坛。有许多关于该主题的文章和博客文章。 DI 当然具有准确记录正在使用的依赖项的通常优点。更容易理解和测试等。问题中的代码确实显示了从 Symfony 基本控制器扩展的控制器,但它不需要。事实上,如果开发人员尝试使用任何基本辅助函数,他们很快就会发现需要将 setContainer 添加到服务定义中。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-02-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多