Как использовать __construct () в контроллере и получить доступ к Securty.Context? - PullRequest
18 голосов
/ 16 марта 2012

У меня проблемы с Symfony2. А именно, как использовать функцию __construct (). Официальная документация шокирующе плоха!

Я хочу использовать следующее:

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

Как только я получаю следующую ошибку:

Неустранимая ошибка: невозможно вызвать конструктор в /Sites/src/DEMO/DemoBundle/Controller/Frontend/HomeController.php в строке 11

Строка 11 - это "parent :: __ construct ();"

Я удалил его и получил следующее, новая ошибка

Неустранимая ошибка: вызов функции-члена get () для необъекта в /Sites/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Controller/Controller.php в строке 242

Я думаю Возможно, мне нужно настроить DIC ContainerInterface, но я понятия не имею, как это сделать (я попытался и потерпел неудачу, с треском)

Любые люди идеи?

Обновление - Попытался изменить расширение ContainerAware и получил эту ошибку:

Неустранимая ошибка: класс DEMO \ DemoBundle \ Controller \ Frontend \ HomeController не может расширяться из интерфейса Symfony \ Component \ DependencyInjection \ ContainerAwareInterface в /Sites/src/DEMO/DemoBundle/Controller/Frontend/HomeController.php.

Используя следующий код в контроллере:

<?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;
     }

Ответы [ 7 ]

23 голосов
/ 16 марта 2012

Я предполагаю, что вы расширяете контроллер Symfony по умолчанию? Если это так, то при взгляде на код откроется ответ:

namespace Symfony\Bundle\FrameworkBundle\Controller;

use Symfony\Component\DependencyInjection\ContainerAware;

class Controller extends ContainerAware
{

Обратите внимание, что не определена конструкция Controller :: __, поэтому использование parent :: __ никуда вас не приведет. Если мы посмотрим на ContainerAware:

namespace Symfony\Component\DependencyInjection;

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

Опять же, конструктор и контейнер недоступны, пока не будет вызван setContainer. Поэтому переопределите setContainer и поместите туда свою логику. Или же просто создайте отдельный контроллер, который не расширяет базовый класс контроллера, и вставьте ваши зависимости непосредственно в конструктор.

Обновление: август 2017

Все еще получаю несколько показов. Если вы действительно хотите выполнить что-то перед каждым контроллером, используйте прослушиватель контроллера ядра. Если все, что вам нужно, это пользователь, то, конечно, используйте getUser (). И, пожалуйста, не переопределяйте setContainer (). В некоторых случаях это сработало бы, но просто запутало ваш код.

4 голосов
/ 17 марта 2012

Мне также часто требуется экземпляр текущего пользователя в большинстве моих контроллеров. Я считаю, что проще всего сделать что-то вроде этого:

class SomeController extends Controller
{
    protected $user;

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

Однако это слишком упрощенный пример. Если вы хотите проделать дополнительную работу до запуска действия контроллера, я предлагаю вам определить ваш контроллер как службу .

Также взгляните на эту статью: Отойдя от базового контроллера

1 голос
/ 03 июля 2013

Я должен получить менеджер 'фасадов' для ресурса моего остальных API. Не использовать конструктор и использовать приватную функцию кажется мне самым простым и простым.

/**
 * 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 Это нормально для меня, чтобы использовать частные / защищенные функции в моем контроллере, если он содержит нулевые условия

0 голосов
/ 10 ноября 2016

Существует только два решения этой проблемы:

  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, где я помещаю код, который я использую, в более чем одно действие в контроллере или который занимает ненужные строки (например, код для создания и заполнения форм, или другой код).выполнять тяжелые задачи или задачи, требующие большого количества кода).

Таким образом, я поддерживаю код в действии, понятном в его целях, не добавляя путаницы.

Возможно использование свойствахранить зависимость - это переоптимизация, но ... мне это нравится:)

0 голосов
/ 22 апреля 2016

Я знаю, что вопрос очень старый, но я не нашел ответа до сих пор.Так что я поделюсь им.

Цель здесь - выполнять код каждый раз, когда вызывается действие в нашем контроллере.

Метод __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 не будет вызван.

0 голосов
/ 26 марта 2015

Как я вижу, Контроллер расширяет ContainerAware , и если мы рассмотрим ContainerAware , он реализует ContainerAwareInterface . Итак, ContainerAware должен объявить точные методы в своем интерфейсе. Добавьте эту строку

публичная функция __construct ();

к определению ContainerAwareInterface , и оно будет решено.

0 голосов
/ 22 апреля 2013

Вы не можете вызывать getUser () или get () для служб в конструкторах контроллеров.Если вы помните об этом, вы сэкономите много времени на отладку.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...