Background:
I am trying to conditionally load routes based on the request host. I have a database setup that has hosts in it that map to templates. If a user comes in from the host A
and that uses template TA
I want to load the routes for that template. If they come in from host B
then load the routes for that template ( TB
).
The reason I have to do this is because each template will share many routes. There are however some unique routes for a given template.
It would be fine to restrict each template routes to a given host, except that there are literally 1000's of hosts.
What I Have Tried:
I have tried a custom route loader as described in the documentation here:
http://symfony.com/doc/current/cookbook/routing/custom_route_loader.html
However when i configure the service and try and inject the "@request"
the constructor fails because $request is null
services:
acme_demo.routing_loader:
class: Acme\DemoBundle\Routing\ExtraLoader
arguments: ["@request"]
tags:
- { name: routing.loader }
Class:
<?php
namespace: Acme\DemoBundle\Routing;
use Symfony\Component\HttpFoundation\Request;
class ExtraLoader
{
protected $request;
public function __construct(Request $request)
{
$this->request = $request;
}
// ...
}
This also doesnt work if I try and switch "@request"
for "@service_container"
then call
$this->container->get('request');
The closest I got to getting this working was following a guide found here:
http://marcjschmidt.de/blog/2013/11/30/symfony-custom-dynamic-router.html
The problem i have with this on is im trying to use annotation
on a controller and i cant seem to get the Symfony\\Component\\Routing\\Loader\\AnnotationFileLoader
working.
Ok I have finally figured out a working solution, Its a mix of several of the above mentioned guides.
Im using a kernel request listener:
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 }
The Listener:
<?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;
}
}
}
}
The Key parts of this are:
Loader
- I found "@sensio_framework_extra.routing.loader.annot_dir"
in the source code. That the annotation directory loader that symfony uses by default so thats the one that I want to use too. But if you want to use a different loader there are others available. Router
- This is what i use to get all of the current routes. NOTE that the $allRoutes->addCollection($routes)
call is on a seperate line. Im not sure why it makes a difference but calling it all in 1 like was not working. $template['routes']
is just a namespaces controller reference like you would use to add routing in your routing.yml. Something like: "@NamespaceBundle/Controller"
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.