Laravel路由扩展:如何让RouteRegistrar支持自定义Route macro?
我已为Illuminate\Routing\Route添加了description宏,实现代码如下:
\Illuminate\Routing\Route::macro('description', function (string $description): AppServiceProvider|\Illuminate\Routing\Route { $this->action['description'] = isset($this->action['description']) ? $this->action['description'].$description : $description; return $this; });
通过该宏,我可以在路由定义中链式调用设置描述:
Route::post('/test', [TestController::class, 'test'])->description('this is a test description');
后续可通过以下方式正常获取描述内容:
$request->route()->getAction('description');
当前遇到的问题:RouteRegistrar的$allowedAttributes为硬编码配置,导致给Route添加的description宏无法通过RouteRegistrar进行前置链式调用(例如无法使用Route::description('xxx')->post('/test', ...)的写法)。
方法1:扩展RouteRegistrar的允许属性(反射实现)
通过反射修改RouteRegistrar的$allowedAttributes数组,将description加入其中,使其支持原生的链式调用语法:
use Illuminate\Routing\RouteRegistrar; use ReflectionClass; // 在AppServiceProvider的boot方法中执行 $registrar = app(RouteRegistrar::class); $reflection = new ReflectionClass($registrar); $property = $reflection->getProperty('allowedAttributes'); $property->setAccessible(true); $currentAttributes = $property->getValue($registrar); $currentAttributes[] = 'description'; $property->setValue($registrar, $currentAttributes);
修改完成后,即可使用Route::description('测试描述')->post('/test', [TestController::class, 'test'])的写法,描述会自动存入路由的action数组中,原有的description宏仍可正常使用(支持多次调用拼接描述)。
方法2:给RouteRegistrar添加对应macro
为RouteRegistrar添加description宏,暂存描述内容,在路由创建时自动应用到Route实例:
use Illuminate\Routing\RouteRegistrar; use Illuminate\Routing\Events\RouteRegistered; // 在AppServiceProvider的boot方法中 // 给RouteRegistrar添加macro RouteRegistrar::macro('description', function (string $description) { $this->attributes['description'] = $description; return $this; }); // 监听路由注册事件,将暂存的描述应用到Route实例 Event::listen(RouteRegistered::class, function (RouteRegistered $event) { $route = $event->route; if (isset($route->getAction()['description'])) { $route->description($route->getAction()['description']); } });
此方法保持了原宏的拼接逻辑,同时支持通过RouteRegistrar链式调用。
方法3:直接使用原生action方法
如果不需要额外的宏逻辑,可直接通过Laravel原生的action方法设置描述,无需修改任何核心类:
Route::post('/test', [TestController::class, 'test'])->action([ 'description' => 'this is a test description' ]);
后续仍可通过$request->route()->getAction('description')正常获取内容。
方法4:自定义RouteRegistrar并替换(持久化方案)
创建自定义的RouteRegistrar类,继承原生类并扩展allowedAttributes:
namespace App\Routing; use Illuminate\Routing\RouteRegistrar as BaseRouteRegistrar; class RouteRegistrar extends BaseRouteRegistrar { protected $allowedAttributes = [ 'as', 'domain', 'middleware', 'name', 'namespace', 'prefix', 'where', 'description', ]; }
在RouteServiceProvider中替换原生的RouteRegistrar绑定:
use App\Routing\RouteRegistrar; use Illuminate\Routing\Router; public function register() { $this->app->bind(Router::class, function ($app) { return new Router($app['events'], $app); }); $this->app->bind(\Illuminate\Routing\RouteRegistrar::class, function ($app) { return new RouteRegistrar($app['router']); }); }
此方案为持久化修改,无需每次启动都通过反射调整,适合长期使用。
内容的提问来源于stack exchange,提问作者Marinario Agalliu

