Не удалось получить доступ к контейнеру Symfony2 в контроллере, расширяющем Symfony \ Bundle \ FrameworkBundle \ Controller \ Controller

Оригинальный вопрос

Я читал каждую страницу « книги » об контейнерах обслуживания, и я все еще сбиваюсь с толку, потому что вещи кажутся случайным образом не срабатывающим почти каждый раз, когда я пытаюсь использовать $this->container . Например, я создаю форму в своем настраиваемом контроллере пакетов в соответствии с инструкциями .

Мой контроллер обычно расширяет базовый контроллер:

 namespace Gutensite\ArticleBundle\Controller; use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Symfony\Component\HttpFoundation\Request; use Gutensite\ArticleBundle\Entity\Article; class AdminEditController extends Controller { public function indexAction() { $content = new Article(); $form = $this->createFormBuilder($content) ->add('content', 'text'); // same issue with the shortcut to the service which I created according the instructions // $form = $this->createForm('myForm', $myEntity) //...more code below... } } 

Это приводит к ошибке:

 Fatal error: Call to a member function get() on a non-object in /vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Controller/Controller.php on line 176 

Если мы посмотрим на этот файл на этом номере строки, мы увидим код Symfony:

 public function createFormBuilder($data = null, array $options = array()) { return $this->container->get('form.factory')->createBuilder('form', $data, $options); } 

Итак, ПОЧЕМУ собственный контроллер Symfony НЕ может получить доступ к функции container-> get () ?!

Что я делаю не так?

Вдоль этих же строк я не могу понять, почему иногда я не могу получить доступ к контейнеру через контейнер $ this-> в моем собственном контроллере (если расширять контроллер фреймворка или ссылаться на него, передавая его в конструкцию и т. Д.), , Кажется случайным …


Предыстория проекта и структура кода

Я создаю CMS, у которой есть маршруты пользователя (URL), хранящиеся в базе данных. Таким образом, у меня есть один маршрут, который определяет все запросы к моему основному контроллеру CMS:

 gutensite_cms_furl: # Match Multiple Paths (the plain / path appears necessary) path: / path: /{furl} defaults: { _controller: GutensiteCmsBundle:Init:index } # Allow / in friendly urls, through more permissive regex requirements: furl: .* 

InitController просматривает запрошенный URL-адрес и получает правильный объект Route, который указывает на объект View, который определяет, какой Bundle и Controller загружается для конкретного запрашиваемого типа страницы, например маршрут для /Admin/Article/Edit указывает на тип контента, который связанный с набором статей и контроллером AdminEdit, который затем создает новый объект для этого типа контента ( Gutensite\ArticleBundle\Controller\AdminEditController.php ) и выполняет необходимые функции. Затем он вставляет необходимые переменные обратно в основной ViewController, который передается шаблону, который будет отображаться на странице.

Этот главный контроллер расширяет контроллер Symfony, и я подтвердил, что контейнер доступен в этом контроллере, например, $this->container->get('doctrine') .

 // Gutensite\CmsBundle\Controller\InitController.php namespace Gutensite\CmsBundle\Controller; use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Symfony\Component\HttpFoundation\Request; use Gutensite\CmsBundle\Entity; class InitController extends Controller { public function indexAction(Request $request, $furl) { // Confirm container is accessible (yes it is) $test = $this->container->get('doctrine'); // Look up the View Entity based on the Route Friendly URL: $furl $viewController = $this->container->get('gutensite_cms.view'); $viewController->findView($furl, $siteId); // Load the Requested Bundle and Controller for this View $path = $viewController->view->namespace_controller."\\".$viewController->view->controller; $content = new $path; // Execute the main function for this content type controller, which adds variables back into the $viewController to be passed to the template. $content->indexAction($viewController); return $this->render( $viewController->view->bundle_shortcut.'::'.$viewController->view->getTemplatesLayout(), array('view' => $viewController) ); } } 

FYI, ViewController определяется как глобальная служба:

 services: gutensite_cms.view: class: Gutensite\CmsBundle\Controller\ViewController arguments: [ "@service_container" ] 

А затем Ниже приведен упрощенный вариант Gutensite/CmsBundle/Controller/ViewController.php

 namespace Gutensite\CmsBundle\Controller; use Doctrine\ORM\EntityManager; use Symfony\Component\DependencyInjection\ContainerInterface as Container; class ViewController { protected $container; public $routing; public $view; public function __construct(Container $container) { $this->container = $container; } public function findView($furl, $siteId=NULL) { $em = $this->container->get('doctrine')->getManager(); $this->routing = $em->getRepository('GutensiteCmsBundle:Routing\Routing')->findOneBy( array('furl'=>$furl, 'siteId'=>$siteId) ); if(empty($this->routing)) return false; // If any redirects are set, don't bother getting view if(!empty($this->routing->getRedirect())) return FALSE; // If there is not view associated with route if(empty($this->routing->getView())) return FALSE; $this->view = $this->routing->getView(); $this->setDefaults(); } } 

Вернувшись в InitController.php мы InitController.php объект вида и загрузили правильный набор и функцию контроллера. В этом случае он загружает `Gutensite \ ArticleBundle \ Controller \ AdminEditController.php, где мы теряем доступ к контейнеру службы.

 namespace Gutensite\ArticleBundle\Controller; use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Symfony\Component\HttpFoundation\Request; use Gutensite\ArticleBundle\Entity\Article; class AdminEditController extends Controller { protected $request; public function __contstruct(Request $request) { $this->request = $request; } public function indexAction($view) { // TEST: Test if I have access to container (I do not) //$doctrine = $this->container->get('doctrine'); // This loads createForm() function from the Symfony Controller, but that controller then doesn't have access to container either. $form = $this->createForm('view', $content); } } 

Более конкретный вопрос

Поэтому я ПРИНИМАЮ, что если вы расширите Symfony Controller, который сам расширяет ContainerAware, этот объект будет «знать о контейнере». Но это, очевидно, не так. И это то, что мне нужно понять лучше. Я предполагаю, что контейнер должен быть введен вручную, но почему? И это стандартный метод?

Solutions Collecting From Web of "Не удалось получить доступ к контейнеру Symfony2 в контроллере, расширяющем Symfony \ Bundle \ FrameworkBundle \ Controller \ Controller"

ОК. Ваше предположение о том, что просто создание объекта ContainerAware автоматически приведет к вводу инъекции, неверно. Новый оператор PHP ничего не знает о зависимостях. Задача контейнера для инъекций зависимостей – заботиться о том, чтобы автоматически вводить материал. И, конечно, вы не используете контейнер для создания своих контроллеров.

Достаточно легко установить:

 $path = $viewController->view->namespace_controller."\\".$viewController->view->controller; $content = new $path; $content->setContainer($this->container); $content->indexAction($request,$viewController); 

Я действительно не слежу за вашим потоком. Представление выглядит как-то наоборот, но я надеюсь, вы сможете увидеть, где и как контейнер вводится в контроллер Symfony. Не делайте ничего в конструкторе контроллера, который полагается на контейнер.

================================================== =============

Вместо использования нового оператора вы можете использовать контейнер службы.

 $contentServiceId = $viewController->view->contentServiceId; $content = $this->container->get($contentServiceId); $content->indexAction($request,$viewController); 

Вместо того, чтобы вы вернули имя класса, верните ему служебный идентификатор. Затем вы настраиваете свой контроллер в services.yml и уходите. Эта запись в кулинарной книге может немного помочь: http://symfony.com/doc/current/cookbook/controller/service.html

================================================== ===========

Все ContainerAware делает это, чтобы заставить Symfony DependencyInjectContainer вводить контейнер. Больше ничего. Не меньше. Вы можете прочитать здесь: http://symfony.com/doc/current/components/dependency_injection/index.html, чтобы получить общее представление о том, что такое инжекционный контейнер зависимости и зависимость от инжектора.