【问题标题】:Setting $this->something in class body, can't use constructor dependency injection在类主体中设置 $this->something,不能使用构造函数依赖注入
【发布时间】:2013-09-16 06:22:13
【问题描述】:

我有一个使用 data_provider 服务的 Symfony 控制器。我不知道如何初始化这个服务。

我试过了:

class DefaultController extends Controller {
    public $dataProvider=$this->get('data_provider');

这会导致错误,不能在 Controller 中使用构造函数,所以我只能:

public $dataProvider=false;
public function someAction(){
  $this->dataProvider=$this->get('data_provider');
...
public function anotherAction(){
  $this->dataProvider=$this->get('data_provider');
...

所以我每次都必须在控制器动作函数中设置它。有没有一种简单的方法可以在创建控制器时初始化 dataProvider?

该服务仅适用于这个包,因此它在 Symfony/src/mmt/myBundle/Resources/config/services.yml 中定义,并且该文件由 Symfony/src/mmt/myBundle/DependencyInjection/myExtension.php 加载。不确定这是否会有所不同,但我更喜欢不需要更改捆绑包外文件的东西。

使用 symfony 2.3.4

[更新]

在看似无穷无尽的指令列表中,只涵盖了您需要做的不到一半的指令,我让注射部分开始工作。感谢大家给我很好的建议。

  1. 我的服务是我的捆绑包的一部分,不想更改捆绑包之外的配置文件来加载它。为了确保 Symfony/src/mmt/mrBundle/Resources/config/services.yml 被加载,你需要一个名为 Symfony/src/mmt/mrBundle/DependencyInjection/mmtmrExtension.php 的文件(不,不要使用任何名称与您的应用程序和捆绑名称相关的 php 文件)。

解释了该文件中的内容here。我不需要在那里做任何事情,因为它是在我创建捆绑包时创建的,并让它创建大部分文件。 (创建捆绑包在标准文档中)

2.

在 services.yml 文件中添加了 data_provider 服务:(阅读有关使用原则设置数据库的标准文档)

data_provider:
    class: mmt\mrBundle\Services\dataProvider
    arguments: [ @doctrine.orm.entity_manager ]

内容:Symfony/src/mmt/mrBundle/Services/dataProvider.php

<?php
namespace mmt\mrBundle\Services;
class dataProvider 
{
    protected $em;
    public function __construct($em){
        $this->em = $em;
    }
    public function getItem($id){
        $item = $this->em->getRepository('mmtmrBundle:Item')
            ->find($id);
        return $item;
    }
    public function saveItem($item){
        $this->em->persist($item);
        $this->em->flush();
    }
}
?>

现在我有了服务,我可以像这样在控制器中使用它:

$this->get("data_provider")->getItem(22);

但我希望我的 DefaultController 在创建 DefaultController 时有一个 $this->dataProvider。最好是一个取决于 dev、prod 和 test。

引入了依赖注入。将以下内容添加到 Symfony/src/mmt/mrBundle/Resources/config/services.yml

mmt.mr.DefaultController:
     class: mmt\mrBundle\Controller\DefaultController
     arguments: [@data_provider]
     calls: 
         - [ "setContainer", [ @service_container ] ]

现在在你的路由中使用 mmt.mr.DefaultController:indexAction(不要使用 mmtmrBundle:Default:index):

/var/www/html/Symfony/src/mmt/mrBundle/Resources/config/routing.yml

mmtmr_homepage:
    path:  /{id}
    requirements:
      id: \d+
    defaults: { _controller: mmt.mr.DefaultController:indexAction, id: false }

在 Symfony/src/mmt/mrBundle/Controller/DefaultController.php 中应该是这样的:

<?php
namespace mmt\mrBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Session\Session;
class DefaultController extends Controller {
    public $dataProvider;
    public function __construct($data_provider){
        $this->dataProvider = $data_provider;
    }
    public function indexAction($id) {
        $item=$this->dataProvider->getItem($id);
        return $this->render('mmtmrBundle:Default:index.html.twig', 
                array('item' => $item));
    }
}
?>

我想就是这样,缺少的东西请告诉我。恭喜;您现在知道如何注入依赖项(如果您还没有)。坏消息是,当您阅读本文时,它可能已经过时,您必须访问 Symfony 站点。那里的文档很好,但没有提到任何对我造成破坏的事情。

【问题讨论】:

  • 您确定您将routing.yml 定义为{ _controller: m.y.DefaultController:indexAction } 并清除了缓存吗?
  • @touki 更改了路由器,现在调用了该方法(选择在 services.yml 中调用)。因为这给了我一个错误,所以我添加了 __construct 函数但得到了同样的错误:Error: Call to a member function get() on a non-object in /var/www/html/Symfony/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Controller/Controller.php line 252
  • 是的,您需要在服务声明中添加calls: [ "setContainer", [ @service_container ] ],以便将服务容器注入控制器。
  • @Touki 根据文档再次选择了构造函数,但看起来在动作函数和构造函数中 $this 指的是 DefaultController 以外的东西。尝试在构造函数中设置 $DataSource:$this-&gt;$dataProvider = $data_provider; 导致 Cannot access empty property 将其留空是没有用的,因为整个练习的重点是设置 $this->dataProvider
  • @Touki 抱歉,忘记了 $this-&gt;$dataProvider 不是有效的 PHP 访问属性。

标签: php symfony dependency-injection


【解决方案1】:

您应该使用依赖注入将其注入控制器。

根据您问题中的课程,您可以执行以下操作:

Symfony/src/mmt/mrBundle/Resources/config/services.yml

   mmt.mr.DefaultController:
         class: mmt\mrBundle\Controller\DefaultController
         calls: 
             - [ setContainer, [ @service_container ] ]
             - [ setDataProvider, [ @data_provider ] ]

Symfony/src/mmt/mrBundle/Controller/DefaultController.php

public function setDataProvider($provider){
    if($this->dataProvider===false){
        $this->dataProvider=$provider;
    }
}

确保使用服务名称,然后使用路由器中的操作,例如:

Symfony/src/mmt/mrBundle/Resources/config/routing.yml

mmtmr_homepage:
    path:  /{id}
    requirements:
      id: \d+
    defaults: { _controller: mmt.mr.DefaultController:indexAction, id: false }

mmt.mr.DefaultController 是 services.yml 中使用的名称,:indexAction 是 DefaultController.php 中名为 indexAction 的函数

【讨论】:

  • 感谢您的回答,我创建了一个基于创建事件侦听器并在 kernel.controller 上调用方法的解决方案,但我更喜欢这个。 setDataProvider 和@mydataprovider 的值是多少?我认为可能 setDataProvider 是 DefaultController 中的一个公共函数,它设置了 $this->dataProvider。那么@mydataprovider 会是dataprovider 的类名,symfony 会创建一个新的吗?所以如果是m\yBundle\DB\dbProvider.php 那么@mydataprovider 就是@m.yBundle.DB.dbProvider?
  • 并非如此。当调用 $this->get 时,你实际上是在从你的容器中请求一个服务/对象。您正在给它您想要获取的项目的密钥。因此,您必须将 @mydataprovider 替换为密钥:@data_provider。你可能想阅读关于 Symfony 依赖注入的章节:symfony.com/doc/current/book/service_container.html
  • 这似乎是一个正确的方法。可能值得一读 - How to define a Controller as service
  • 嗨 Michiel,我已经更新了我的问题。我没有收到任何错误,但是当我通过请求索引页面调用 DefaultController 的 indexAction 时什么也没有发生。我期待 DefaultController 的 setDataProvider 方法被我的 dataProvider 类的新实例调用。
  • @Touki 谢谢你的链接,太糟糕了,它会产生一个错误,我已经用结果更新了我的问题。
【解决方案2】:

我建议使用返回服务的方法。

类似:

public function getDataProvider()
{
    return $this->get('data_provider');
}

并创建一个扩展 Symfony2 控制器的“AdvancedController”,将这个方法放入其中,然后让所有控制器扩展它。

在 AdvancedController 中,您可以将您使用的所有全局方法放入控制器中,这真的很舒服。

【讨论】:

  • 感谢您的回答,我每次使用$this-&gt;dataProvider 时都会调用getDataProvider() 吗?还是每次需要时都必须调用 $this->getDataprovider() ?我倾向于 yml 注入,特别是如果我可以为 prod、dev 和 test 设置不同的值。
  • 每次需要的时候可以调用方法,比如:$dataProvider = $this-&gt;getDataProvidet()或者$data = $this-&gt;getDataProvider()-&gt;getData();
  • 这种方法不是最好的。在您的情况下,您应该使用依赖注入。在下一篇文章中描述了如何注入它。您必须注入它并在控制器构造函数中获取类。
  • 谢谢 Edmund,我正在尝试注射。让 data_provider 服务工作,但无法将其注入 DefaultController 服务。
【解决方案3】:

使用依赖注入后,$data_provider 必须正确地提供给您的控制器,所以您为什么不使用它:

public function dataInit($data_provider){
  $logger = $this->get('logger');
  $logger->notice('from dataInit so this works');
}

也许它必须是:

public function dataInit($data_provider){
  $logger = $data_provider->getLogger();
  $logger->notice('from dataInit so this works');
}

如果没有,请粘贴您的 dataProvider 类

【讨论】:

  • 我已经更新了这个问题,希望不是一切都像现在这样难。链接symfony.com/doc/current/cookbook/controller/service.html 大部分时间都让我到达那里,但不知何故,控制器在您将其设为服务后不再是控制器,并且如果您不在路由器中使用服务名称,则永远不会调用构造函数(两者均未在文章中提及)。对于不是控制器的控制器,我必须将`[“setContainer”,[@service_container]]`添加到我的services.yml中,并且对于不被调用的构造函数,我必须更改routing.yml。非常感谢 taoki
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-04-20
  • 2011-02-02
  • 2011-09-02
  • 2013-01-30
  • 2018-06-17
相关资源
最近更新 更多