Symfony2 - Как использовать __construct() в контроллере и получить доступ к Securty.Context?
У меня возникают проблемы с 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
Я думаю, мне, возможно, понадобится настроить ContainerInterface DIC, но я не знаю, как это сделать (я пробовал и терпел неудачу, жалко)
Любые идеи?
Обновить. Пробовал сменить расширение ContainerAware и получил эту ошибку:
Неустранимая ошибка: класс DEMO\DemoBundle\Controller\Frontend\HomeController не может распространяться на интерфейс Symfony\Component\DependencyInjection\ContainerAwareInterface в /Sites/src/DEMO/DemoBundle/Controller/Frontend/HomeController.php в строке 43
Используя следующий код в контроллере:
<?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;
}
Ответы
Ответ 1
Я предполагаю, что вы расширяете контроллер Symfony по умолчанию? Если это так, посмотрите на код, который покажет ответ:
namespace Symfony\Bundle\FrameworkBundle\Controller;
use Symfony\Component\DependencyInjection\ContainerAware;
class Controller extends ContainerAware
{
Обратите внимание, что конструктор Controller:: __ не определен, поэтому использование parent:: __ construct не приведет вас ни к чему. Если мы посмотрим на ContainerAware:
namespace Symfony\Component\DependencyInjection;
class ContainerAware implements ContainerAwareInterface
{
protected $container;
public function setContainer(ContainerInterface $container = null)
{
$this->container = $container;
}
}
Опять же, никакой конструктор и контейнер недоступны до вызова setContainer. Поэтому переопределите setContainer и поместите туда свою логику. Или просто создайте автономный контроллер, который не расширяет базовый класс контроллера и не вводит ваши зависимости непосредственно в конструктор.
Обновление Aug 2017
По-прежнему получается несколько хитов. Если вы действительно хотите что-то выполнить перед каждым контроллером, используйте прослушиватель контроллера ядра. Если все, что вам нужно, это пользователь, то, конечно, используйте getUser(). И, пожалуйста, не переопределяйте setContainer(). В некоторых случаях это сработает, но это просто свернет ваш код.
Ответ 2
Я также часто хочу экземпляр текущего пользователя на большинстве моих контроллеров. Я считаю, что проще всего сделать что-то вроде этого:
class SomeController extends Controller
{
protected $user;
public function getUser()
{
if ($this->user === null) {
$this->user = $this->get('security.context')->getToken()->getUser();
}
return $this->user;
}
}
Однако это слишком упрощенный пример. Если вы хотите сделать больше работы до того, как действие контроллера будет запущено, я предлагаю вам определить ваш контроллер как службу.
Также взгляните на эту статью: Переход от базового контроллера
Ответ 3
Мне нужно восстановить менеджер "facade" для моего ресурса api rest. Не использовать конструктор и использовать закрытую функцию, кажется, проще и проще для меня.
/**
* 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 Мне нормально использовать частные/защищенные функции в моем контроллере, если он содержит нулевые условные обозначения
Ответ 4
Вы не можете вызвать getUser() или get() для служб в конструкторах контроллера. Если вы помните это, вы сэкономите много времени отладки.
Ответ 5
Я знаю, что вопрос очень старый, но до сих пор я не нашел ответа. Поэтому я поделюсь им.
Цель здесь - выполнить код каждый раз, когда вызывается действие в нашем контроллере.
Метод __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 actions are called.
return call_user_func_array(array($this, $method), $args);
}
}
Внимание! Вы должны определить каждый метод как частный метод! Или магический метод __call
не будет вызван.
Ответ 6
Решить эту проблему можно только двумя способами:
-
Используйте частный метод, как указано @Tjorriemorrie здесь. Но это грязный метод для пуристов. (Я использую это!: D);
-
Определите контроллер как службу, но таким образом вы потеряете все ярлыки, предоставленные 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
, где я помещаю код, который я использую более чем в одном действии в контроллере, или который неумело занимает строки (например, код для создания и заполнения форм или другого кода для выполнять тяжелые задачи или задачи, требующие большого количества кода).
Таким образом, я делаю код в действии ясным в своих целях, не добавляя путаницы.
Может быть, использование свойства для хранения зависимостей является переоптимизацией, но... мне оно нравится:)
Ответ 7
Как я вижу, Контроллер расширяет ContainerAware, и если мы посмотрим на ContainerAware, он реализует ContainerAwareInterface, Таким образом, ContainerAware должен был указать точные методы в этом интерфейсе. Добавьте эту строку
публичная функция __construct();
в определение ContainerAwareInterface, и оно будет разрешено.