$this->контейнер равен НУЛЮ в контроллере на Symfony3
У меня возникла досадная проблема, когда я вызываю это в контроллере (clientdomaincontroller):
$this->getDoctrine()->getManager();
Я получил эту ошибку:
Call to a member function has() on null
Я посмотрел трассировку стека и вижу, что:
$this->container is null
Мой контроллер расширяется из компонента контроллера Symfony:
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
Самое забавное, что в другом контроллере (HomeController) Я делаю точно такие же вещи:
- Расширение от контроллера (точно такого же класса)
- Получить доктрину
- Получить Менеджер сущностей
- Используйте менеджера
И это без какой-либо ошибки.
Единственное различие между домашним контроллером и контроллером домена клиента заключается в том, что второй является сервисом. Поэтому я написал это в службах.файл yml:
services:
client_domain:
class: AppBundle\Controller\ClientDomainController
Наконец, я протестировал многие вещи, такие как создание конструктора для моего контроллера и добавление его в сервисы.файл yml (вещи, которые a никогда не делал с функциональным файлом):
arguments: [ 'doctrine.orm.entity_manager' ]
3 answers
Когда вы регистрируете свой контроллер в качестве службы, Symfony создает его точно так, как вы ему говорите.
Таким образом, разница в том, что, хотя ваш контроллер реализует ContainerAwareInterface
(через расширение класса Controller
), в конце концов никто не вызывает метод setContainer
для использования этого интерфейса и установки значения $container
. Вы должны сделать это вручную в своих сервисах.конфигурация yml, например:
calls:
- [ setContainer, [ @service_container ] ]
Но это не лучшее решение
Регистрация ваших контроллеров в качестве служб хороша в главный. Это делает их более проверяемыми и ремонтопригодными.
Но это верно до тех пор, пока вы придерживаетесь хороших правил ООП. В этом случае, когда вы передаете весь контейнер, это означает, что:
- Ваш экземпляр контроллера может иметь недопустимое состояние, если вы не передадите контейнер (или вам следует учесть, что он может быть установлен не везде, где вы его используете), что плохо по замыслу.
- Это трудно проверить, так как вам нужно имитировать весь контейнер, а не только зависимости, которые этот контроллер использует.
- Зависимости явно не определены, так как вам нужно заглянуть в код контроллера, чтобы узнать, какие зависимости извлекаются из контейнера.
Короче говоря, зависимости должны передаваться через contrustor, как вы сделали в конце, или вы можете использовать внедрение зависимостей на основе действий, когда зависимость используется только в этом конкретном действии.
На самом деле лучшим решением было бы даже не расширять базовый класс Controller
, чтобы вы контроллеры независимы от платформы.
Ваш контроллер должен быть таким:
class HelperController extends Controller
{
/**
* @var \Symfony\Component\DependencyInjection\ContainerInterface
*/
protected $container;
/**
* HelperController constructor.
*
* @param ContainerInterface $container
*/
public function __construct(ContainerInterface $container)
{
$this->container = $container;
}
}
И в файле конфигурации (в моем случае xml)
<service id="xxx.helper_controller" class="xxx\EspacePROBundle\Controller\HelperController">
<argument type="service" id="service_container" />
</service>
Я надеюсь, что это поможет.
Как указано в ответе Якуба Матчака, контейнер DI для контроллера не установлен.
В качестве альтернативы добавлению раздела calls
в ваши сервисы.yml вы можете вызвать setContainer
в конструкторе класса, в котором вы хотите использовать контроллер.
use App\Controller\MyController;
use Symfony\Component\DependencyInjection\ContainerInterface;
class MyClass {
/**
* The injected controller
* @var MyController
*/
protected $my_controller;
public function __construct(MyController $my_controller, ContainerInterface $container) {
$this->my_controller = $my_controller;
$this->my_controller->setContainer($container);
}
}