我已经阅读了" 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,那么该对象就会知道容器"。但显然事实并非如此。这就是我需要更好地理解的东西。我假设容器必须手动注入,但为什么呢?那是标准方法吗?
答案 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只是为了了解依赖注入和依赖注入器容器的基本概念。