如何创建一个自定义的路由加载器
什么是自定义的路由加载器 ¶
自定义路由加载器(Custom Route Loader)使你能够基于一些约定或pattern(条件)来生成路由。这种用法的一个很好的例子是FOSRestBundle ,它的路由是基于控制器的action方法名称来生成的。
即便使用自定义的路由加载器,你仍然需要手动修改路由配置(如app/config/routing.yml
)。
现在的很多bundle都在使用他们自己的路由加载器去完成以上面描述的情况,例如,FOSRestBundle,JMSI18nRoutingBundle,KnpRadBundle 和 SonataAdminBundle。
加载路由 ¶
路由在symfony程序中是通过DelegatingLoader
来加载的。这个加载器使用了若干其他loader(delegates/代理)来加载不同类型的资源,如YAML文件和控制器中的@Route
和@Method
注释。这个专用的加载器实现了LoaderInterface
,因此产生了两个重要的方法:supports()
和load()
。
从Symfony标准版的routing.yml
中拿到这几行:
1 2 3 4 |
# app/config/routing.yml
app:
resource: '@AppBundle/Controller/'
type: annotation |
当主加载器解析它时,会尝试遍历所有被注册的委托加载器,然后把给定的资源(@AppBundle/Controller/
)和类型(type,如annotation
等)作为参数,调用 supports()
方法。当其中的一个加载器返回true
时,load()
方法会被调用,它应该返回一个含有一组Route
对象的RouteCollection
。
以这种方式加载的路由,将被路由器(Router)给缓存起来。定义在默认格式中(XML、YML、PHP文件)的路由,都将以这种方式来操作。
创建一个自定义的加载器 ¶
为了从一些自定义资源(例如,从annotations,YAML 或XML文件以外的资源)中加载路由,你需要创建一个自定义的路由加载器。加载器要实现LoaderInterface
接口。
大多数情况下,应继承Loader
而不是自行实现 LoaderInterface
。
下面的加载器例程支持加载extra
类型的路由资源。这个类型名称不能与其他加载器所支持的资源类型相同以免冲突。你要做的只是取一个你想要的具体名称。资源名称本身并未真的用在示例中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
// src/AppBundle/Routing/ExtraLoader.php
namespace AppBundle\Routing;
use Symfony\Component\Config\Loader\Loader;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;
class ExtraLoader extends Loader
{
private $loaded = false;
public function load($resource, $type = null)
{
if (true === $this->loaded) {
throw new \RuntimeException('Do not add the "extra" loader twice');
}
$routes = new RouteCollection();
// prepare a new route
// 准备一个新路由
$path = '/extra/{parameter}';
$defaults = array(
'_controller' => 'AppBundle:Extra:extra',
);
$requirements = array(
'parameter' => '\d+',
);
$route = new Route($path, $defaults, $requirements);
// add the new route to the route collection
// 添加新路由到route collection
$routeName = 'extraRoute';
$routes->add($routeName, $route);
$this->loaded = true;
return $routes;
}
public function supports($resource, $type = null)
{
return 'extra' === $type;
}
} |
确保你指定的控制器真实存在。在这个例子中你要去创建一个AppBundle
的ExtraController
控制器,里面有extraAction
方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
// src/AppBundle/Controller/ExtraController.php
namespace AppBundle\Controller;
use Symfony\Component\Httpfoundation\Response;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class ExtraController extends Controller
{
public function extraAction($parameter)
{
return new Response($parameter);
}
} |
现在定义一个ExtraLoader
服务:
1 2 3 4 5 6 |
# app/config/services.yml
services:
app.routing_loader:
class: AppBundle\Routing\ExtraLoader
tags:
- { name: routing.loader } |
1 2 3 4 5 6 7 8 9 10 11 |
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<services>
<service id="app.routing_loader" class="AppBundle\Routing\ExtraLoader">
<tag name="routing.loader" />
</service>
</services>
</container> |
1 2 3 4 5 6 7 8 9 |
use Symfony\Component\DependencyInjection\Definition;
$container
->setDefinition(
'app.routing_loader',
new Definition('AppBundle\Routing\ExtraLoader')
)
->addTag('routing.loader')
; |
注意这个routing.loader
标签。使用这个标签的所有服务都将被标记为潜在的路由加载器,并且会被作为专门的路由加载器被添加到 routing.loader
服务中,该服务是DelegatingLoader
的实例。
使用自定义加载器 ¶
如果你没有做任何其他事,你自定义的路由加载器将不会 被调用。剩下要做的,就是将下面几行添加到路由配置中:
1 2 3 4 |
# app/config/routing.yml
app_extra:
resource: .
type: extra |
1 2 3 4 5 6 7 |
<?xml version="1.0" encoding="UTF-8" ?>
<routes xmlns="http://symfony.com/schema/routing"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing/routing-1.0.xsd">
<import resource="." type="extra" />
</routes> |
1 2 3 4 5 6 7 |
// app/config/routing.php
use Symfony\Component\Routing\RouteCollection;
$collection = new RouteCollection();
$collection->addCollection($loader->import('.', 'extra'));
return $collection; |
这里的type
键极为重要。它的值应该是“extra”,因为ExtraLoader
所支持的就是这个类型,而且这还能确保ExtraLoader
的load()
方法被调用。这个resource
键对于ExtraLoader
来说是无足轻重的,所以被设置为“.”。
被定义为使用自定义路由加载器的路由,将被框架自动缓存。因此,每当你在“加载器“的类中做出修改时,不要忘记清除缓存。
更为先进的加载器 ¶
如果你自定义的路由加载器和前例一样继承自Loader
,你还可以利用Loader类所提供的解析器(resolver),一个LoaderResolver
实例,来加载次级路由资源(secondary routing resources):
当然你仍然需要去实现supports()
和 load()
方法。只要你想去加载其他资源时 - 例如一个YAML路由配置文件 - 你就要调用import()
方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
// src/AppBundle/Routing/AdvancedLoader.php
namespace AppBundle\Routing;
use Symfony\Component\Config\Loader\Loader;
use Symfony\Component\Routing\RouteCollection;
class AdvancedLoader extends Loader
{
public function load($resource, $type = null)
{
$collection = new RouteCollection();
$resource = '@AppBundle/Resources/config/import_routing.yml';
$type = 'yaml';
$importedRoutes = $this->import($resource, $type);
$collection->addCollection($importedRoutes);
return $collection;
}
public function supports($resource, $type = null)
{
return 'advanced_extra' === $type;
}
} |
资源名称和导入的路由配置的类型可以是任何东西,通常应该是被路由配置加载器所支持的(如,YAML、XML、PHP、annotation等)。