如何添加需要服务方法的路由要求?
How can I add a route requirement which requires a service method?
我需要建立一条具有动态条件的路线。
目前,我只是使用 requirements
来匹配静态列表中的单词:
/**
* @Route(
* "/{category}/{id}",
* requirements={
* "category"="^(foo|bar)$"
* }
* )
*
* ...
*/
但现在我需要从服务方法中动态检索这些词。
在寻找解决方案的过程中,给了condition
expression language一个希望;但唯一的
此处可访问的变量是上下文和请求。但是,为了实现我的目标,我
需要对容器服务的完全访问权限。
换句话说,我想执行以下伪php以测试路由:
if (in_array($category, MyService::getAllCategories())) {
/* Inform Symfony that the route matches (then use this controller) */
} else {
/* Inform Symfony that the route does not match and that the routing process
* has to go on. */
}
请注意我的问题的主要原因是 {category}
参数放在前面
url,然后可以混淆其他路由。然后我不能只在里面测试我的状况
controller 和 return 404 如果不需要条件。我当然可以把这条路线放在
在路由过程顺序中结束,但我认为这不是一个好的解决方案。
... parameter is placed early in the url, and then can offuscate other routes.....
尽管上面的内容让我感到困惑,但希望我没有误解,这就是您所需要的。至少我是这么知道的!
- 您需要一个自定义注释class。例如
namespace App\Annotation:Category
- 上面的 class 将接受来自 custom 注释条目的参数。例如
@Category
- 您的 custom 事件侦听器会将它们连接在一起以使其工作。例如
namespace App\Event\Listener:CategoryAnnotationListener
This is a full example 涵盖方法和 class 级别自定义注释。似乎您只需要方法级别,所以这是您的示例。根据您的需要重构。 注意: 已测试并且有效。
用法
declare(strict_types=1);
namespace App\Controller;
use App\Annotation\Category;
/**
* @Route("/{category}/{id}")
* @Category
*/
public function index...
类别
namespace App\Annotation;
/**
* @Annotation
*/
class Category
{
}
监听器
declare(strict_types=1);
namespace App\Event\Listener;
use App\Annotation\Category;
use Doctrine\Common\Annotations\Reader;
use ReflectionClass;
use ReflectionException;
use RuntimeException;
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
class CategoryAnnotationListener
{
private $annotationReader;
public function __construct(Reader $annotationReader)
{
$this->annotationReader = $annotationReader;
}
public function onKernelController(FilterControllerEvent $event): void
{
if (!$event->isMasterRequest()) {
return;
}
$controllers = $event->getController();
if (!is_array($controllers)) {
return;
}
$this->handleAnnotation($controllers, $event->getRequest()->getPathInfo());
}
private function handleAnnotation(iterable $controllers, string $path = null): void
{
list($controller, $method) = $controllers;
try {
$controller = new ReflectionClass($controller);
} catch (ReflectionException $e) {
throw new RuntimeException('Failed to read annotation!');
}
$method = $controller->getMethod($method);
$annotation = $this->annotationReader->getMethodAnnotation($method, Category::class);
if ($annotation instanceof Category) {
$this->doYourThing($path);
}
}
private function doYourThing(string $path = null): void
{
// Explode $path to extract "category" and "id"
// Run your logic against MyService::getAllCategories()
// Depending on the outcome either throw exception or just return 404
}
}
配置
services:
App\Event\Listener\CategoryAnnotationListener:
tags:
- { name: kernel.event_listener, event: kernel.controller, method: onKernelController }
自定义路由加载器可能是一个解决方案...http://symfony.com/doc/current/routing/custom_route_loader.html此示例生成的不是动态路由,但工作正常。
仅作为示例,假设 CategoryProvider 和 Category 是您的 类 ...
<?php
// src/Routing/CategoryLoader.php
namespace App\Routing;
use Symfony\Component\Config\Loader\Loader;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;
use App\CategoryProvider;
class CategoryLoader extends Loader
{
private $isLoaded = false;
private $catProvider;
public function __construct(CategoryProvider $catProvider)
{
$this->catProvider = $catProvider;
}
public function load($resource, $type = null)
{
if (true === $this->isLoaded) {
throw new \RuntimeException('Do not add the "extra" loader twice');
}
$routes = new RouteCollection();
foreach ($this->catProvider->getAll() as $cat) {
// prepare a new route
$path = sprintf('/%s/{id}', $cat->getSlug());
$defaults = [
'_controller' => 'App\Controller\ExtraController::extra',
];
$requirements = [
'parameter' => '\d+',
];
$route = new Route($path, $defaults, $requirements);
// add the new route to the route collection
$routeName = 'categoryRoute' . $cat->getSlug();
$routes->add($routeName, $route);
}
$this->isLoaded = true;
return $routes;
}
public function supports($resource, $type = null)
{
return 'extra' === $type;
}
}
我需要建立一条具有动态条件的路线。
目前,我只是使用 requirements
来匹配静态列表中的单词:
/**
* @Route(
* "/{category}/{id}",
* requirements={
* "category"="^(foo|bar)$"
* }
* )
*
* ...
*/
但现在我需要从服务方法中动态检索这些词。
在寻找解决方案的过程中,给了condition
expression language一个希望;但唯一的
此处可访问的变量是上下文和请求。但是,为了实现我的目标,我
需要对容器服务的完全访问权限。
换句话说,我想执行以下伪php以测试路由:
if (in_array($category, MyService::getAllCategories())) {
/* Inform Symfony that the route matches (then use this controller) */
} else {
/* Inform Symfony that the route does not match and that the routing process
* has to go on. */
}
请注意我的问题的主要原因是 {category}
参数放在前面
url,然后可以混淆其他路由。然后我不能只在里面测试我的状况
controller 和 return 404 如果不需要条件。我当然可以把这条路线放在
在路由过程顺序中结束,但我认为这不是一个好的解决方案。
... parameter is placed early in the url, and then can offuscate other routes.....
尽管上面的内容让我感到困惑,但希望我没有误解,这就是您所需要的。至少我是这么知道的!
- 您需要一个自定义注释class。例如
namespace App\Annotation:Category
- 上面的 class 将接受来自 custom 注释条目的参数。例如
@Category
- 您的 custom 事件侦听器会将它们连接在一起以使其工作。例如
namespace App\Event\Listener:CategoryAnnotationListener
This is a full example 涵盖方法和 class 级别自定义注释。似乎您只需要方法级别,所以这是您的示例。根据您的需要重构。 注意: 已测试并且有效。
用法
declare(strict_types=1);
namespace App\Controller;
use App\Annotation\Category;
/**
* @Route("/{category}/{id}")
* @Category
*/
public function index...
类别
namespace App\Annotation;
/**
* @Annotation
*/
class Category
{
}
监听器
declare(strict_types=1);
namespace App\Event\Listener;
use App\Annotation\Category;
use Doctrine\Common\Annotations\Reader;
use ReflectionClass;
use ReflectionException;
use RuntimeException;
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
class CategoryAnnotationListener
{
private $annotationReader;
public function __construct(Reader $annotationReader)
{
$this->annotationReader = $annotationReader;
}
public function onKernelController(FilterControllerEvent $event): void
{
if (!$event->isMasterRequest()) {
return;
}
$controllers = $event->getController();
if (!is_array($controllers)) {
return;
}
$this->handleAnnotation($controllers, $event->getRequest()->getPathInfo());
}
private function handleAnnotation(iterable $controllers, string $path = null): void
{
list($controller, $method) = $controllers;
try {
$controller = new ReflectionClass($controller);
} catch (ReflectionException $e) {
throw new RuntimeException('Failed to read annotation!');
}
$method = $controller->getMethod($method);
$annotation = $this->annotationReader->getMethodAnnotation($method, Category::class);
if ($annotation instanceof Category) {
$this->doYourThing($path);
}
}
private function doYourThing(string $path = null): void
{
// Explode $path to extract "category" and "id"
// Run your logic against MyService::getAllCategories()
// Depending on the outcome either throw exception or just return 404
}
}
配置
services:
App\Event\Listener\CategoryAnnotationListener:
tags:
- { name: kernel.event_listener, event: kernel.controller, method: onKernelController }
自定义路由加载器可能是一个解决方案...http://symfony.com/doc/current/routing/custom_route_loader.html此示例生成的不是动态路由,但工作正常。
仅作为示例,假设 CategoryProvider 和 Category 是您的 类 ...
<?php
// src/Routing/CategoryLoader.php
namespace App\Routing;
use Symfony\Component\Config\Loader\Loader;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;
use App\CategoryProvider;
class CategoryLoader extends Loader
{
private $isLoaded = false;
private $catProvider;
public function __construct(CategoryProvider $catProvider)
{
$this->catProvider = $catProvider;
}
public function load($resource, $type = null)
{
if (true === $this->isLoaded) {
throw new \RuntimeException('Do not add the "extra" loader twice');
}
$routes = new RouteCollection();
foreach ($this->catProvider->getAll() as $cat) {
// prepare a new route
$path = sprintf('/%s/{id}', $cat->getSlug());
$defaults = [
'_controller' => 'App\Controller\ExtraController::extra',
];
$requirements = [
'parameter' => '\d+',
];
$route = new Route($path, $defaults, $requirements);
// add the new route to the route collection
$routeName = 'categoryRoute' . $cat->getSlug();
$routes->add($routeName, $route);
}
$this->isLoaded = true;
return $routes;
}
public function supports($resource, $type = null)
{
return 'extra' === $type;
}
}