Symfony2-如何在控制器中使用__construct()并访问Securty.Context


Symfony2 - How to use __construct() in a Controller and access Securty.Context?

我在使用Symfony2时遇到了一些问题。即如何使用__construct()函数。官方文件糟糕得令人震惊!

我希望能够使用以下内容:

public function __construct()
{
    parent::__construct();
    $user = $this->get('security.context')->getToken()->getUser();
}

我如何得到以下错误:

致命错误:无法在第11行的/Sites/src/DEMO/DemoBundle/Controller/Frontend/HomeController.php中调用构造函数

第11行是"parent::__construct();"

我删除了它,得到了以下,新的错误

致命错误:在第242行的/Sites/vendor/symfony/symfoy/src/symfony/Bundle/FrameworkBundle/Controller/Controller.php中的非对象上调用成员函数get()

认为我可能需要设置ContainerInterface DIC,但我不知道如何做到这一点(我尝试过,但失败了,很惨)

有什么想法吗?

更新-尝试更改以扩展Containerware并得到此错误:

致命错误:类DEMO''DemoBundle''Controller''Frontend''HomeController无法从第43行上/src/DemoBundle/Controller/Frontend/HomeController.php中的接口Symfony''Component''DependencyInjection''ContainerwareInterface扩展

在控制器中使用以下代码:

<?php
namespace DEMO'DemoBundle'Controller'Frontend;
use Symfony'Component'DependencyInjection'ContainerAware;
class HomeController extends ContainerAwareInterface
{
     protected $container;
     public function setContainer(ContainerInterface $container = null)
     {
         $this->container = $container;
     }

我假设您正在扩展默认的Symfony控制器?如果是这样的话,看看代码就会发现答案:

namespace Symfony'Bundle'FrameworkBundle'Controller;
use Symfony'Component'DependencyInjection'ContainerAware;
class Controller extends ContainerAware
{

请注意,没有定义Controller::__construct,因此使用parent::__construction不会有任何结果。如果我们看看ContainerWare:

namespace Symfony'Component'DependencyInjection;
class ContainerAware implements ContainerAwareInterface
{
    protected $container;
    public function setContainer(ContainerInterface $container = null)
    {
        $this->container = $container;
    }
}

同样,在调用setContainer之前,没有构造函数和容器是不可用的。所以重写setContainer并将您的逻辑放在那里。或者只制作一个独立的控制器,它不扩展基本控制器类,并将依赖项直接注入构造函数。

2017年8月更新

仍然有一些点击。如果您真的想在每个控制器之前执行一些东西,那么请使用内核控制器侦听器。如果您只需要用户,那么当然可以使用getUser()。请不要重写setContainer()。在某些情况下,它会起作用,但只会使代码复杂化。

我也经常希望在我的大多数控制器中有一个当前用户的实例。我发现做这样的事情最简单:

class SomeController extends Controller
{
    protected $user;
    public function getUser()
    {
        if ($this->user === null) {
            $this->user = $this->get('security.context')->getToken()->getUser();
        }
        return $this->user;
    }
}

然而,这是一个过于简单化的例子。如果您想在控制器操作开始之前做更多的工作,我建议您将控制器定义为服务。

还可以看看这篇文章:离开基本控制器

我必须为我的rest api的资源检索"facade"管理器。对我来说,不使用构造函数和使用私有函数似乎是最简单的

/**
 * Class ExchangesController
 * @RouteResource("Exchange")
 */
class ExchangesController extends Controller
{
    /**
     * Get exchange manager
     * @return ExchangeManager
     */
    protected function getExchangeManager()
    {
        return $this->get('exchange_manager');
    }
    /**
     * @ApiDoc(
     *  description="Retrieve all exchanges",
     *  statusCodes={
     *    200="Successful"
     *  }
     * )
     */
    public function cgetAction()
    {
        return $this->getExchangeManager()->findAll();
    }

PS我可以在控制器中使用私有/受保护的函数,只要它包含零条件

您不能为控制器构造函数中的服务调用getUser()或get()。如果您记住这一点,您将节省大量的调试时间。

我知道这个问题很老了,但直到现在我才找到答案。所以我会分享的。

这里的目标是在每次调用控制器中的操作时执行代码

__construct方法不起作用,因为它是在调用其他方法之前调用的,所以您无法访问服务容器。

诀窍是在调用每个方法时自动过载:

<?php
namespace AppBundle'DefaultController;
class DefaultController extends Controller {
    private function method1Action() {
        return $this->render('method1.html.twig');
    }
    private function method2Action() {
        return $this->render('method2.html.twig');
    }
    public function __call($method, $args) {
         $user = $this->get('security.tokenStorage')->getToken()->getUser();
         // Do what you want with the User object or any service. This will be executed each time before one of those controller's actions are called.
        return call_user_func_array(array($this, $method), $args);
    }
}

警告!您必须将每个方法定义为一个私有方法!否则就不会调用__call魔术方法。

这个问题只有两种解决方案:

  1. 使用@Tjorriemorrie在这里指出的私有方法。但对于纯粹主义者来说,这是一种肮脏的方法。(我在用这个!:D);

  2. 将控制器定义为服务,但这样会丢失Symfony'Bundle'FrameworkBundle'Controller'Controller提供的所有快捷方式。这是一篇展示如何做到这一点的文章。

就我个人而言,我更喜欢这样的解决方案:

class MyController extends Controller
{
    /** @var AwesomeDependency */
    private $dependency;
    public function anAction()
    {
         $result = $this->getDependency();
    }
    /**
     * Returns your dependency.
     */
    private function getDependency()
    {
        if (null === $this->dependency)
            $this->dependency = $this->get('your.awesome.dependency');
        return $this->dependency;
    }
}

这通常是一个我称之为MyManager的类,我将在控制器中的多个操作中使用的代码或无效占用行的代码(例如,用于创建和填充表单的代码,或用于执行繁重任务或需要大量代码的任务的其他代码)放在该类中。

通过这种方式,我在操作中保持代码的目的清晰,不会增加混乱。

也许使用属性来存储依赖关系是过度优化,但是。。。我喜欢它:)

如我所见,Controller扩展了Containerware,如果我们看一下containerwareontainerwareInterface。因此,ContainerWare必须在其接口中声明了确切的方法。添加此行

公共函数__construct()

ContainerwareInterface定义,它将被解决。