在控制器构造函数上加载容器服务,Symfony

Loading container service on Controller constructor, Symfony

有没有办法在控制器构造函数中加载容器服务,比如

class PostController extends Controller
{

    protected $breadcrumb;

    public function __construct()
    {
        //initializing breadcrumb
        $breadcrumbs = $this->get("white_october_breadcrumbs");
        $breadcrumbs->addRouteItem("Dashboard", "adminPage");
        $breadcrumbs->addRouteItem("Post", "postPage");
        $this->breadcrumb = $breadcrumbs;
    }
//..
}

您正在扩展 Symfony\Bundle\FrameworkBundle\Controller\Controller class,它通过 setter 注入(方法 setContainer)而不是通过构造函数参数注入 container

虽然您在 __construct() 中,但您还没有可用的 $container。因此,您的解决方案是覆盖 setContainer 方法(由服务容器调用,在构造控制器之后立即调用)并将您的逻辑放在那里(而不是 __construct())。

class PostController extends Controller
{

    protected $breadcrumb;

    public function setContainer(ContainerInterface $container = null)
    {
        parent::setContainer($container);
        $breadcrumbs = $container->get("white_october_breadcrumbs");
        $breadcrumbs->addRouteItem("Dashboard", "adminPage");
        $breadcrumbs->addRouteItem("Post", "postPage");
        $this->breadcrumb = $breadcrumbs;
    }

}

其他解决方案,需要多一点工作,是 define your controller as a service 并设置它,使其获得 container 作为构造函数参数

是的,这可以通过 Controller as a Service 实现。但是,注入 Container 被认为是一个不好的例子。如果你想获得服务white_october_breadcrumbs,你可以在你的控制器服务定义中注入它;如果你使用 yaml:

service:
    class: app.controller.my_controller
    arguments:
        - "@white_october_breadcrumbs"
public function __construct(Breadcrumbs $breadcrumbs)
{
    $this->breadcrumbs = $breadcrumbs;
}

这是一个更好的解决方案,因为您不希望您的应用程序尽可能依赖于容器。

考虑到您希望在多个页面上使用此功能,另一种解决方案是使用事件侦听器或什至在操作上使用自定义注释来动态添加面包屑。但是不建议初学者这样做。

Loading container service on Controller constructor, Symfony

不要。您的服务的生命周期是多少?如果调用控制器的操作,您想初始化一些面包屑吗?子请求呢?控制器的创建顺序是什么?

1.任一中央配置:

  1. 创建一个配置映射(例如在 yml 中),控制器 class 名称作为键,面包屑作为值(例如路由数组)
  2. 创建一个BreadcrumbBuilder服务,注入配置
  3. 为您的视图创建 BreadcrumbProvider 独立服务和 twig-extension
  4. 创建并注册一个 BreadcrumbRequestListener http://api.symfony.com/2.7/Symfony/Component/HttpKernel/Event/FilterControllerEvent.html 侦听器,注入 BreadcrumbBuilder,`BreadcrumbProvider
  5. 根据 getController()-callable(示例 A)创建面包屑
  6. 直接在 twig 中访问面包屑

样本 A:

$controller = $event->getController();
if(\is_array($controller)) {
  $clazz = \get_class($controller[0]);
  $breadcrumbs = $this->breadcrumbBuilder->build($clazz);
  $this->breadcrumbProvider->setBreadcrumbs($breadcrumbs);
}

2。或 Open-Closed 控制器,因此您无需更改中央配置,只需添加控制器即可:

  1. 让每个提供面包屑的控制器成为 BreadcrumbProviderInterface 实现 getBreadcrumb() 返回创建面包屑所需的一些数据
  2. 同时创建一个 BreadcrumbRequestListener,检查该接口并存储可通过 twig(示例 B)访问的引用

样本 B:

$controller = $event->getController();
if(\is_array($controller) && $controller[0] instanceof BreadcrumbProviderInterface) {
  $this->breadcrumbs->setCurrentProvider($controller[0]);
}

3。其他想法:

  • 通过遍历 request_stack 来请求面包屑,控制器和操作在 $request->attributes->get('_controller'); 上进行。您可以只使用主请求或遍历所有控制器的所有面包屑
  • 基地控制器class

我有一个类似的案例。这是我的场景: 我有一个第三方包 class,在 vendor/my-vendor/my-third-party-bundle/Resources/config/services.xml 中声明为服务,我想将其注入到我的 src/AppBundle/Controller/MyController 中。命名空间设置正确,当我在上面控制器的某些操作方法中从容器中获取我的第三方包 class 时,一切正常。例如: class MyController extends Controller { public function myAction() { // ... $myThirdPartyBundleService = $this->container ->get('my_third_party_bundle_service.class'); // ... } } 我尝试的是按照官方 (Symfony 文档)[http://symfony.com/doc/current/cookbook/controller/service.html] 将控制器声明为服务,因为我在控制器中多次使用该服务。 似乎在 MyThirdPartyBundle\DependencyInjection\MyThirdPartyBundleExtension::load() 中我有服务构建。我还尝试将 die('my-third-party-bundle'); 放在 load() 方法中,在 MyAppBundle\Controller\MyControlle::__construct() 中我尝试 die('controller') 只是为了查看先加载哪个。正如我所料,首先返回的结果是来自第三方包的结果。我还在 MyAppBundle\DependencyInjection\MyAppBundleExtension::load(); 中尝试了 die('my_app_bundle');。正如我所尝试的,结果取决于捆绑包在 AppKernel 中声明的顺序。所以我将 MyThirdPartyBundle 更改为在 MyAppBundle 之前加载。而且我仍然没有得到想要的结果,而是 NULL。似乎此时我的第三方包的 class 尚未构建。我没有太多时间检查为什么会发生这种情况,但我怀疑原因是容器的配置构建。所以我能做的最简单快速的事情就是覆盖 MyAppBundle 控制器中的 Symfony\Component\DependencyInjection\ContainerInterface::setContainer()```。这不是解决问题的最优雅的方法,但它确实有效。我认为正确的方法是将控制器声明为服务,但它似乎不适用于我的情况。