在 Laravel 中封装路由注解
在 Laravel 中,你可以在 routes
目录下创建路由的声明文件,例如 web.php
或者 api.php
。这篇文章描述了另一种方式:通过扫描注解来注册路由。
示例
为了简化路由的编写,可以通过注解的方式来显示路由,也避免了编写路由需要跳转文件。先来看示例:
#[GetMapping(path: '/user/register')]
public function register() {}
看上去,使用注解会比在文件中单独声明要来的方便。
实现
首先你可能要创建一个注解类:
#[Attribute(Attribute::TARGET_METHOD)]
class GetMapping
{
public function __construct(
public string $path,
public string $prefix = 'api'
) {}
}
接着,注解注解就是注释加上解释。肯定要在某一个时间点,去解析注释,你可以在 web.php
或者 api.php
中去解析注解。核心逻辑如下:
- 扫描控制器目录下
app\Http\Controllers
中的.php
结尾的代码文件 - 遍历每一个控制器中的每一个方法,利用反射读取是否包含
#[GetMapping]
之类的注解 - 如果包含路由注解,则获取注解中的参数,调用
Route
门面类自动注册路由
扫描控制器目录下的文件
我们先来看第 1 步,扫描控制器目录下的代码文件, 示例代码如下:
public static function scanForFiles(string $basePath): array
{
$directoryIterator = new RecursiveDirectoryIterator($basePath);
$recursiveIterator = new RecursiveIteratorIterator($directoryIterator);
$controllerFiles = [];
foreach ($recursiveIterator as $file) {
if ($file->isDir() || in_array($file->getFilename(), '.php')) {
continue;
}
if (str_contains($file->getFilename(), 'Controller.php')) {
$controllerFiles[] = $file->getPathname();
}
}
return $controllerFiles;
}
遍历控制器文件
接着,就是遍历控制器下的文件, 转换文件名到命名空间
foreach ($controllerFiles as $file) {
// 只是说明一个思路,命名空间转换的代码就不贴出来了
$controller = ControllerHelper::convertPathToNamespace($file);
$reflectionClass = new ReflectionClass($controller);
$methods = $reflectionClass->getMethods(ReflectionMethod::IS_PUBLIC);
foreach ($methods as $method) {
// todo: 扫描注解,自动注册路由
}
}
扫描注解注册路由
最后一步,就是扫描注解注册路由了:
$attribute = collect($method->getAttributes())
->filter(fn (ReflectionAttribute $attribute) => $attribute->getName() === GetMapping::class)->first();
$uri = $attribute->newInstance()->path;
Route::{$routesMapping[$attribute->getName()]}($uri, [$controller, $method->getName()]);
到这里就完成了,还是那句话,封装是允许复杂的,但是使用要简单 。
总结
这篇文档介绍了如何自己封装路由注解,给出一种实现的思路。
当然你可能觉得不够灵活,如果要我加上一个路由中间件呢?有两种做法:
- 给上面这个
#[GetMapping]
增加一个类,支持传入一个中间件的类的数组 - 单独实现一个路由中间件的注解
此外,你还可以实现其他的注解 #[PostMapping]
、#[PutMapping]
、#[DeleteMapping]
、#[RequestMapping]
、#[RestController]
等等,只要你有需要。
这些注解只是用来作为路由的注解吗?其实不是,还可以作为接口文档生成的注解。你可以利用 VitePress 等静态网页的生成方案,解析这些注解,生成 Markdown 文件,然后生成静态的 API 接口的站点,接入 CI/CD…… 只要你敢想敢为,就可以任意发挥。
对了,你还可以用这类注解来自动生成 SDK,如果你的服务是对外的话,比如实现准备好 PHP、Java、Go、Python 的 SDK 项目基础代码,然后通过扫描这些注解,然后生成对应的请求类的代码,就实现了。
在微服务中,你可以利用这些注解,生成每一个服务的 SDK,然后通过 Composer 提供给其他的服务依赖。连版本化也一并解决了。
本作品采用《CC 协议》,转载必须注明作者和本文链接
使用
\Illuminate\Support\Facades\Route::addRoute('GET', '/test', function () { return 'Hello World'; });
而不是
Route::{$routesMapping[$attribute->getName()]}($uri, [$controller, $method->getName()]);
路由缓存怎么办?