无法在扩展Symfony \ Bundle \ FrameworkBundle \ Controller \ Controller的控制器中访问Symfony2容器

时间:2014-04-23 00:06:41

标签: php symfony containers

原始问题

我已经阅读了" book"的所有页面。关于服务容器,我仍然感到困惑,因为几乎每次我尝试使用$this->container时,事情似乎随机都不起作用。例如,我在instructions之后的自定义捆绑控制器中构建表单。

我的控制器像往常一样扩展基本控制器:

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的路由指向与Article捆绑包和AdminEdit控制器关联的内容类型,然后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)
    );


    }
}

仅供参考,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我们检索了视图对象并加载了正确的bundle和controller函数。在这种情况下,它加载了`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控制器,它本身扩展了ContainerAware,那么该对象就会知道容器"。但显然事实并非如此。这就是我需要更好地理解的东西。我假设容器必须手动注入,但为什么呢?那是标准方法吗?

1 个答案:

答案 0 :(得分:2)

确定。您只是使ContainerAware对象自动导致容器被注入的假设是不正确的。 PHP new运算符对依赖项一无所知。依赖注入容器的工作是负责自动注入东西。当然,您没有使用容器来创建控制器。

很容易修复:

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

我真的不关注你的流程。视图的东西似乎对我不利,但我相信你可以看到容器注入Symfony控制器的位置和方式。不要在依赖于容器的控制器构造函数中执行任何操作。

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

您可以使用服务容器,而不是使用new运算符。

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

不要让您查看返回类名,而是让它返回服务ID。然后,您可以在services.yml中配置控制器,然后关闭。这本食谱可能会有所帮助:http://symfony.com/doc/current/cookbook/controller/service.html

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

所有ContainerAware都是为了让Symfony DependencyInjectContainer注入容器。而已。没什么。您可以考虑阅读这里:http://symfony.com/doc/current/components/dependency_injection/index.html只是为了了解依赖注入和依赖注入器容器的基本概念。