baraja-core / smart-router
Requires
- php: ^8.0
- baraja-core/localization: ^2.0
- baraja-core/url: ^1.0
- nette/application: ^3.0
- nette/caching: ^3.0
- nette/http: ^3.0
Requires (Dev)
- phpstan/phpstan: ^0.12.74
- phpstan/phpstan-nette: ^0.12.14
- roave/security-advisories: dev-master
- tracy/tracy: ^2.8
This package is auto-updated.
Last update: 2022-04-21 12:17:45 UTC
README
路由器是一个在URL和应用请求之间进行双向转换的组件。双向转换意味着可以从URL推导出展示器动作,反之亦然,生成对应动作的URL。
此软件包提供SmartRouter
的默认实现,它改进了Nette的默认路由方法,并添加了将特定URL及其参数、语言和高级配置存储在数据库(或其他存储库)中的功能。
安装
该软件包是默认Baraja 沙盒的一部分。
或者,您可以通过Composer手动安装
$ composer require baraja-core/smart-router
所有路由规则都定义在RouterFactory
类中,我们可以像这样注入SmartRouter
服务:
public static function createRouter(SmartRouter $smartRouter): RouteList { $router = new RouteList; // Register SmartRouter $router[] = $smartRouter; // Optionally add additional routes or a collection of routes $router[] = self::createFrontRouter(); return $router; }
我们将确保所有请求首先通过SmartRouter处理,只有当失败时才会使用其他常规路由。
SmartRouter
SmartRouter
是我对路由应如何工作的内部实现。整个请求路由过程完全自动进行,并且可以通过非常详细的配置进行影响,然后将其缓存。
我们将路由分为3个独立的部分
Match
:将URL重写到应用请求(请求处理)Construct URL
:基于应用请求创建URLRewriter
:实现URL部分参数重写接口,其中大部分魔法发生在这里
默认的Rewriter实现是DoctrineRewriter
(更多信息),它基于数据库表进行URL路由。其内部实现非常高效。
对于较小应用程序或测试情况,您可以使用StaticRewriter
,它根据存储在数组中的物理数据进行重写。
Match - 接受请求URL
以下文本描述了如何根据加载的URL编译找到的参数列表。
路由分为4个逻辑步骤。
- 在缓存中查找所需的URL。如果记录存在(也可以是负的),我们直接路由它,不再进一步搜索
- 准备缓存以供配置,如果有的话,我们将从缓存中使用
- 根据内部逻辑(以下将描述)查找当前URL(路由)
- 将路由参数保存到键值缓存中,它将在第一步中再次使用。
请求缓存包含2个参数
- 使用常量
CACHE_EXPIRATION
(默认30分钟)过期 - 使用值
route/<presenter>:<action>
进行标记
注意:特殊行为
我们能够处理的所有请求仍然在路由器内部进行处理,路由器会始终添加
locale
键。如果在路由过程中未指定,我们将返回当前站点的默认语言。如果默认语言不存在,我们将返回英语。如果匹配请求的当前语言不存在,我们将抛出
E_NOTICE
级别的错误。
发送和处理匹配的应用程序请求
无论我们在缓存中找到的是应用程序请求还是直接匹配创建的,它都始终通过returnRequest()
方法发送。
此方法的任务是将当前环境(私有属性environment
)设置为路由器的内部状态,这将用于路由其他请求和编译URL。如果当前匹配的环境比内部设置的环境更好,则将仅使用当前匹配的环境。
环境优先级评分表存储在environmentScoreTable
配置键中,基本实现如下:
localhost
: 1beta
: 2production
: 3
同时,此方法的任务是将语言设置为翻译器。
注意:如果在进一步处理应用程序和生成链接时没有路由请求,则无法可靠地确定语言和环境。
例如,如果我们以CLI模式(cron或其他后台任务)生成指向电子邮件的链接,则始终必须将语言(
locale
)和环境(environment
)插入到所有请求中。如果我们不输入这些值,路由器可能的行为与我们预期的不同。
实体MatchRequest
- 请求处理的内部逻辑
MatchRequest
实体作为处理当前URL并返回结果的辅助工具。实例由Smart路由器本身创建。
只有在调用单个公共方法match()
后才会进行最终处理,该方法在成功时返回参数字段,在错误或无效请求时返回null
。
处理内部逻辑可能会随时间改变(并改进),本文只描述了基本原则。具体实现和解释可以直接在实现中的注释中找到。
处理流程
- 查找路由(
processRoute()
方法)
目前我们只支持Front
和Admin
模块,我们正在寻找匹配的presenter
和action
。
我们逐步尝试规则
- 是否为主页或空URL?(
$slug === ''
) - 是否有管理请求?如果有,我们将根据掩码
admin/[<locale=en cs|en>/]<presenter>/<action>
常规路由。 - 基于
Rewriter
的转录+添加参数、环境和语言。 - 正则表达式(对应于
<presenter> / <action>
掩码)用于与旧或lazy
URL的向后兼容。
- 我们将处理请求的语言
请求的最终语言很难确定,因为在设计路由内核时,我们遇到了几十种特殊案例,其中**不容易**做出决定。它通常是域名语言与另一个语言slugu或locale参数的组合。
解决方案在于积累有关当前URL的所有可用语言,根据键将其排序到字段中,然后根据优先级选择最佳语言。
我们区分3个基本优先级级别
- (最佳)
URL参数
:包含在?locale=en
中 路径/别名
:根据重写将URL的一部分分配给特定语言域名
:语言是当前路由域的典型语言。
- 最终参数的编译
在本步骤中,将执行参数的最后检查和清除,并将其作为应用程序请求发送。
最小配置始终包含值
presenter
(形式为<module>:<presenter>
)动作
locale
(字符串)environment
(字符串,值:localhost
,beta
,production
)
构造URL - 构建URL
以下文本描述了根据指定的参数生成URL的方法。
在生成URL本身之前,需要验证基本键的存在,根据这些键我们将确定URL的类型。
关于
locale
:URL可用的语言environment
:URL指向的环境(例如,避免在beta
上生成指向production
或其他组合的链接)
- 根据参数组合的键,我们在缓存中找到URL
- 如果缓存不包含生成的URL,我们将准备配置或从缓存中读取它
- 基于内部逻辑(如下所述)构建所需的URL
- 写入缓存并返回最终的URL,或者在无法崩溃的情况下返回
null
编译URL的过程比匹配更具挑战性,必须考虑更多的规则。然而,某些部分的工作方式相同。
正确生成的URL的基础是其唯一性,即使在将来也是如此。它不得包含逻辑冲突(例如,短语的语种不适合域名语种),因此使用了极其复杂的逻辑和一组规则进行组装。
在构建最终URL之前,创建了一个ConstructUrlRequest
实体的实例,该实例需要(array $params, Rewriter $rewriter, array $config = null)
并设置实体的内部状态,以便根据配置进一步生成。
在编译时,我们将找到以下值
environment
:我们将生成URL的环境(例如,对于localhost或域生产,可能不同),locale
:URL语言lazy
:一个bool
标志,指示一个简单地组装的常规URL,不使用Rewriter
,这可能会减慢速度 - 如下文所述,presenter
和action
用于页面类型上下文。
组装本身仅在调用construct()
方法时进行,该方法返回包含绝对URL的string
或发生错误时返回null
。
程序又有很多步骤
- 获取域名
- 环境是否为空、未知或配置中未为其定义默认域名?那么将保留当前域名。
- 如果环境存在,我们将找到最适合所需语言的域名
- 如果不存在所需语言的域名,我们将返回环境的默认域名,并记住
needLocaleParameter
标志,表示我们必须在URL的参数中传递语言。
在返回域名之前,我们检查配置以查看是否应在域名之前添加www.
(useWww
标志)。
- 处理
path
(slug
)
现在这取决于我们是否根据实际参数(使用Rewriter
接口)构建路径,或者它是一个lazy link
,即简化版本。
简化链接生成形式为<presenter>/<action>
的路径,为Homepage:default
仅生成/
,为Product:default
仅生成/product
等等。
关于实际链接,我们使用 Rewriter
中的 rewriteByParameters()
方法生成 URL,而路由器的内部逻辑则执行进一步的清理,例如比较语言和删除重写的参数。更多详情请参考关于转录器的部分。
- 我们将根据以下通用格式编译最终的 URL
<方案>://<域名>/<路径>?<参数>
懒 URL - 简化 URL 格式
智能路由器支持所谓的 懒 URL
,这是一种使用正则表达式生成的永久 URL,在编译和解析过程中不需要访问 Rewriter
或数据库。
其优点是读取和创建速度极快。特别适用于大型项目列表(如目录中的产品),构建一个 * 美观的 URL * 会花费不必要的长时间,并完全延迟页面加载。
懒 URL 在生成时通过传递值 ['lazy' => true]
来生成。
Rewriter
为了能够动态地将 URL 重新写为应用程序请求(参数)并返回(生成 URL),有必要实现一个 智能路由器
,它提供了一般算法来完成这项任务。
然而,在实际应用中,不同的客户端需要以不同的方式重新编写 URL - 但主要是从数据库中。此 Rewriter
接口用于此任务,在需要更改编译 URL 的方法或从静态文件(小型网站或测试进度)重新编写时,这一点尤为重要。
rewriteByPath(string $path):?array;
在参数字段中覆盖当前路径(别名
)。如果没有,则返回 null
。
最小配置以返回
[
'presenter' => 'Front:Homepage',
'action' => 'default',
'locale' => 'en',
] + other parameters
rewriteByParameters 接口 (array $parameters):?RewriterParametersMatch;
在别名和其他属性上覆盖所需的参数。以类型实体返回结果。
RewriterParametersMatch
实体的任务是携带关于 URL 被重新编写到哪个 path
(别名
),哪种语言以及使用了哪些参数的严格信息。
示例
在路由 /wheels
URL 时,在 Front:Category:detail
上执行了重新编写,参数为 id = 1
。
我们必须将此参数单独传递到字段中,因为路由器将参数 id = 1
覆盖到别名 /kola
中,这明显表示此 ID。删除也是必要的,以便参数不会进一步保留在 URL 中,因为它已经作为别名的部分(在数据库中)传递,因此可以始终重新路由。
📄 许可证
baraja-core/smart-router
使用 MIT 许可证。有关更多详细信息,请参阅 LICENSE 文件。