Symfony 2 如何动态添加访问请求对象的路由


Symfony 2 How To Dynamically Add Routes With Access To The Request Object

背景:

我正在尝试根据请求主机有条件地加载路由。我有一个数据库设置,其中有映射到模板的主机。如果用户来自主机A并且使用模板TA我想加载该模板的路由。如果它们来自主机B则加载该模板的路由(TB(。

我必须这样做的原因是每个模板将共享许多路由。但是,给定模板有一些独特的路由。

每个模板路由限制为给定主机是可以的,除了实际上有 1000 个主机。

我尝试过的:

我已经尝试了自定义路由加载程序,如此处文档中所述:

http://symfony.com/doc/current/cookbook/routing/custom_route_loader.html

但是,当我配置服务并尝试注入"@request"时,构造函数失败$request因为 null

services:
    acme_demo.routing_loader:
        class: Acme'DemoBundle'Routing'ExtraLoader
        arguments: ["@request"]
        tags:
            - { name: routing.loader }

类:

<?php
namespace: Acme'DemoBundle'Routing;
use Symfony'Component'HttpFoundation'Request;
class ExtraLoader
{
    protected $request;
    public function __construct(Request $request)
    {
        $this->request = $request;
    }
    // ...
}

如果我尝试将"@request"切换为"@service_container"然后调用,这也不起作用

$this->container->get('request');

我最接近于使其工作是遵循此处的指南:

http://marcjschmidt.de/blog/2013/11/30/symfony-custom-dynamic-router.html

遇到的问题是我试图在控制器上使用annotation,但我似乎无法让Symfony'Component'Routing'Loader'AnnotationFileLoader工作。

好的,我终于找到了一个可行的解决方案,它是上述几个指南的混合体。

我使用内核请求侦听器:

services:
    website_listener:
        class: NameSpace'Bundle'EventListener'WebsiteListener
        arguments:
            - "@website_service"
            - "@template_service"
            - "@sensio_framework_extra.routing.loader.annot_dir"
            - "@router"
            - "%admin_domain%"
        tags:
            - { name: kernel.event_listener, event: kernel.request, method: onKernelRequest, priority: 33 }

侦听器:

<?php
namespace NameSpace'WebsiteBundle'EventListener;
use NameSpace'TemplateBundle'Service'TemplateService;
use NameSpace'WebsiteBundle'Service'WebsiteService;
use Symfony'Component'HttpKernel'Event'GetResponseEvent;
use Symfony'Component'HttpKernel'Exception'NotFoundHttpException;
use Symfony'Component'Routing'Loader'AnnotationDirectoryLoader;
use Symfony'Component'HttpKernel'EventListener'RouterListener;
use Symfony'Component'Routing'Matcher'UrlMatcher;
use Symfony'Component'Routing'RequestContext;
use Symfony'Component'Routing'RouteCollection;
use Symfony'Bundle'FrameworkBundle'Routing'Router;
use Symfony'Component'PropertyAccess'PropertyAccess;
class WebsiteListener
{
    /**
     * @var WebsiteService
     */
    protected $websiteService;
    /**
     * @var TemplateService
     */
    protected $templateService;
    /**
     * @var AnnotationDirectoryLoader
     */
    protected $loader;
    /**
     * @var Router
     */
    protected $router;
    /**
     * @var string
     */
    protected $adminDomain;
    public function __construct(WebsiteService $websiteService, TemplateService $templateService, AnnotationDirectoryLoader $loader, Router $router, $adminDomain)
    {
        $this->websiteService = $websiteService;
        $this->templateService = $templateService;
        $this->loader = $loader;
        $this->router = $router;
        $this->adminDomain = $adminDomain;
    }
    public function loadRoutes()
    {
        $template = $this->templateService->getTemplateByAlias($this->websiteService->getWebsite()->getTemplate());
        $routes = $this->loader->load($template['routes'],'annotation');
        $allRoutes = $this->router->getRouteCollection();
        $allRoutes->addCollection($routes);
    }
    public function onKernelRequest(GetResponseEvent $event)
    {
        try {
            $this->websiteService->handleRequest($event->getRequest());
            $this->loadRoutes();
        } catch(NotFoundHttpException $e) {
            if($event->getRequest()->getHost() !== $this->adminDomain){
                throw $e;
            }
        }
    }
}

其中的关键部分是:

  • Loader - 我在源代码中找到了"@sensio_framework_extra.routing.loader.annot_dir"。symfony默认使用的注释目录加载器,所以这也是我想使用的那个。但是,如果您想使用不同的加载程序,还有其他可用的加载程序。
  • Router - 这是我用来获取所有当前路线的方法。请注意$allRoutes->addCollection($routes)调用位于单独的线路上。我不确定为什么它会有所不同,但像这样将其全部称为 1 是行不通的。
  • $template['routes']只是一个命名空间控制器引用,就像您在 routeting.yml 中添加路由一样。像这样:"@NamespaceBundle/Controller"