动态路由
Symfony 默认路由器被开发用于处理静态路由定义,因为它们通常在执行之前在配置文件中声明。完整的路由配置被注入到构造函数中。然后,它使用此配置创建一个 UrlMatcher,而不是注入匹配器作为服务。这使得默认路由器不适合处理动态定义的路由。为了处理大量用户创建的路由,此组件包含了 DynamicRouter
,它配置了一个 RequestMatcherInterface 或 UrlMatcherInterface 服务。实际的匹配逻辑取决于您选择的底层匹配器实现。您可以通过将其传递给 DynamicRouter
构造函数来轻松使用您自己的匹配策略。作为此组件的一部分,已经提供了 NestedMatcher。
DynamicRouter 进一步允许使用一组可以轻松配置的 RouteEnhancerInterface
来修改路由匹配的结果参数。
DynamicRouter
也能够从 Route
对象生成 URL。ProviderBasedGenerator
可以生成从 RouteProviderInterface
实例加载的 URL。ContentAwareGenerator
可以从任何实现了 RouteReferrersInterface
的内容对象确定要生成 URL 的 Route
,这意味着您可以直接从内容对象生成 URL。
事件
可选地,您可以为动态路由器提供一个 事件调度器。如果您这样做,它将在匹配过程中触发预匹配事件之一,具体取决于使用的方法,并在生成 URL 之前触发另一个事件。
- cmf_routing.pre_dynamic_match (在
match
方法开始时分发) - cmf_routing.pre_dynamic_match_request (在
matchRequest
方法开始时分发。在 Symfony 完整堆栈框架的上下文中,只会触发此事件。) - cmf_routing.pre_dynamic_generate (在
generate
方法开始时分发)
预匹配事件的类是 Symfony
,生成事件的类是 Symfony
。生成事件还允许您通过更新事件中的值来操作事件中的路由名称、参数和引用类型。
Symfony
类包含事件常量。要了解如何注册事件,请参阅核心文档中的 “`How to create an Event Listener`_”。
匹配器
动态路由器需要注入一个 RequestMatcherInterface 或一个 UrlMatcherInterface。此组件使用 NestedMatcher 提供了一个合适的实现。
路由增强器
可选地,并且在匹配过程之后,DynamicRouter
可以应用一组 RouteEnhancerInterface
实例。路由增强器是一种在框架继续之前操作匹配路由中的参数的方法。它们可以用于,例如,动态分配控制器或通过确定参数或将请求参数“向上转型”为它们对应的对象来将逻辑从控制器中分离出来。
该组件已经提供了一些通用增强器。它们都遵循不更改现有字段,而仅在字段尚不存在时才添加字段的原则。
RouteContentEnhancer
- 如果路由是
RouteObjectInterface
的实例,则此增强器将目标字段设置为getContent()
的返回值。 FieldMapEnhancer
- 配置了键值映射。如果匹配的指定字段包含键,则目标字段设置为值。
FieldByClassEnhancer
- 配置了类名到值的映射。如果指定的字段包含一个对象,该对象是映射中某个类的实例,则将目标字段设置为相应的值。请注意,如果对象是多个类的实例,则采用第一个匹配项。例如,此增强器用于根据 Content 文档的类确定控制器和模板。此增强器类似于
FieldMapEnhancer
,但对映射键执行 instanceof 检查而不是字符串比较。 FieldPresenceEnhancer
- 如果路由匹配中存在某个字段,则在另一个字段尚未设置的情况下,将该字段设置为指定的值。
ContentRepositoryEnhancer
- 如果源字段存在于路由匹配中,则在目标字段尚未设置的情况下,将目标字段设置为
ContentRepositoryInterface
返回的内容,其值为源字段的值。
您还可以通过创建一个实现 Symfony
的类来创建自己的路由增强器。
路由增强器使用 addRouteEnhancer
方法注册,该方法具有可选的第二个参数来提供优先级。
路由增强器编译器 Pass
此组件提供了一个 RegisterRouteEnhancersPass
。如果您使用 Symfony 依赖注入组件,则可以使用此编译器 pass 将所有具有特定标签的增强器注册到动态路由器。
1 2 3 4 5 6 7 8
use Symfony\Cmf\Component\Routing\DependencyInjection\Compiler\RegisterRouterEnhancersPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
// a ContainerBuilder
$container = ...;
$pass = new RegisterRouterEnhancersPass('cmf_routing.dynamic_router', 'dynamic_router_route_enhancer');
$container->addCompilerPass($pass);
在添加 pass 并配置容器构建器之后,您可以继续按照 Symfony DI 组件编译部分 中所述编译容器。
您可以选择配置动态路由器服务名称。当从容器加载动态路由器时,编译器 pass 将修改此服务定义以注册增强器。如果您未指定任何内容,则默认服务名称为 cmf_routing.dynamic_router
。
您还可以使用编译器 pass 构造函数的第二个参数配置要使用的标签名称。如果您不这样做,则默认标签是 dynamic_router_route_enhancer
。如果您正在使用 Symfony CMF RoutingBundle,则此标签已使用默认名称激活。
将路由与内容链接
根据您的应用程序逻辑,请求的 URL 可能具有关联的内容对象。此类 URL 的路由可以实现 RouteObjectInterface
以在存在时返回内容对象。如果您配置了 RouteContentEnhancer
,它会将内容对象插入到 _content
键的匹配数组中。请注意,Route
可能实现 RouteObjectInterface
,但在某些情况下仍然不返回任何模型实例。在这种情况下,将不会设置 _content
字段。
此外,实现此接口的路由还可以提供自定义路由名称。getRouteKey
返回的键将用作路由名称,而不是 Symfony 核心兼容的路由名称,并且可以包含任何字符。例如,这允许您将路径设置为路由名称。与 NestedMatcher
一起提供的 UrlMatcher 都将 _route
键替换为路由实例,并将提供的名称放入 _route_name
中。
所有路由仍然需要扩展 Symfony 组件中的基类 Symfony\\Component\\Routing\\Route。
重定向
您可以通过实现 RedirectRouteInterface
来创建重定向。它可以重定向到绝对 URI、可以由链中的任何 Router 生成的路由名称或另一个 Route
对象。
请注意,实际的重定向逻辑不是由 bundle 处理的。您应该实现自己的逻辑来处理重定向。有关在完整的 Symfony 堆栈下实现该重定向的示例,请参阅 RoutingBundle。
生成 URL
除了将传入的请求匹配到一组参数之外,Router 还负责从路由及其参数生成 URL。ChainRouter
迭代其已知的路由器,直到其中一个能够生成匹配的 URL。
除了 RequestMatcherInterface
和 UrlMatcherInterface
将 Request/URL 匹配到其对应的参数之外,DynamicRouter
还使用 UrlGeneratorInterface
实例,这使其可以从路由生成 URL。
生成器方法如下所示:
1
public function generate(string $name, array $parameters = [], int $referenceType = self::ABSOLUTE_PATH);
在 Symfony 核心中,所有路由都通过名称标识。CMF 路由也可以从路由对象生成 URL。由于 $name
必须是字符串,因此必须使用特殊的 cmf_routing_object
名称,并且路由实例在参数中传递,键为 _route_object
。
ProviderBasedGenerator
扩展了 Symfony 的默认 UrlGenerator (它反过来又实现了 UrlGeneratorInterface),并要求路由提供程序根据名称和参数查找路由。然后,它让核心逻辑从该 Route
生成 URL。
CMF 组件还包括 ContentAwareGenerator
,它扩展了 ProviderBasedGenerator
,用于检查 _route_object
参数是否是实现 RouteReferrersReadInterface
的对象。如果是,则从该对象获取 Route
。使用 ContentAwareGenerator
,您可以通过三种方式为内容生成 URL:
- 或者将
Route
对象作为_route_object
参数传递 - 或者将作为内容的
RouteReferrersInterface
对象作为_route_object
参数传递 - 或者提供
ContentRepositoryInterface
的实现,并将内容对象的 id 作为参数content_id
和cmf_routing_object
作为 $name 传递。
如果您想实现自己的生成器,请实现 VersatileGeneratorInterface
,以便在无法生成路由时获得更好的调试消息。