need
Recently, due to the needs of business functions, it is necessary to match the routing instance according to the request path (such as admin/auth/menu/46/edit
) and request method (such as GET
) recorded in the database, and then do some other things after getting the routing instance.
analyze
In fact, it is the core function of routing (matching a class of request mapping to a callback type variable). The function that comes with the framework itself, the source code search is implemented by the following code blocks:
// Illuminate/Routing/RouteCollection.php public function match(Request $request) { // 1. 获取路由集合$routes = $this->get($request->getMethod()); // 2. 匹配路由$route = $this->matchAgainstRoutes($routes, $request); return $this->handleMatchedRoute($request, $route); } // Illuminate/Routing/AbstractRouteCollection.php protected function matchAgainstRoutes(array $routes, $request, $includingMethod = true) { [$fallbacks, $routes] = collect($routes)->partition(function ($route) { return $route->isFallback; }); return $routes->merge($fallbacks)->first(function (Route $route) use ($request, $includingMethod) { // 3. 遍历匹配return $route->matches($request, $includingMethod); }); } // Illuminate/Routing/Route.php public function matches(Request $request, $includingMethod = true) { $this->compileRoute(); foreach ($this->getValidators() as $validator) { // 4. 遍历验证器验证匹配if (! $includingMethod && $validator instanceof MethodValidator) { continue; } if (! $validator->matches($this, $request)) { return false; } } return true; }
The general logic of the code is: traverse the route collection, throw the current request to the four validators UriValidator
, MethodValidator
, SchemeValidator
, HostValidator
to verify the matches one by one. The validator mainly verifies the pathInfo
, method
, scheme
and host
information of the matching request object.
So I just need to modify the information of the current request object and throw it to the matches
method above to achieve the function I need.
accomplish
Since it is the same project, the scheme
and host
are consistent with the current request and do not need to be modified. And pathInfo
and method
are two private attributes and the method for which the corresponding write permission is not found. So implement a macro method that has the ability to write private properties. The final code is as follows:
<?php namespace App\Support\Macros; use Illuminate\Routing\Route; use Illuminate\Routing\Router; use Illuminate\Support\Arr; use InvalidArgumentException; class RequestMacro { /** * 修改属性*/ public function propertyAware(): callable { return function ($property, $value){ /** @var \Illuminate\Http\Request $this */ if (! property_exists($this, $property)) { throw new InvalidArgumentException('The property not exists.'); } $this->{$property} = $value; return $this; }; } /** * 匹配路由*/ public function matchRoute(): callable { return function ($includingMethod = true){ // 1. 获取路由集合/* @var \Illuminate\Routing\RouteCollection $routeCollection */ $routeCollection = app(Router::class)->getRoutes(); /** @var \Illuminate\Http\Request $this */ $routes = is_null($this->method()) ? $routeCollection->getRoutes() : Arr::get($routeCollection->getRoutesByMethod(), $this->method(), []); [$fallbacks, $routes] = collect($routes)->partition(function ($route){ return $route->isFallback; }); return $routes->merge($fallbacks)->first(function (Route $route) use ($includingMethod){ // 2. 遍历匹配return $route->matches($this, $includingMethod); }); }; } }
Registration request macro
// App\Providers\AppServiceProvider public function register() { Request::mixin($this->app->make(RequestMacro::class)); }
Example of use
$route = request() ->propertyAware('pathInfo', \Illuminate\Support\Str::start('admin/auth/menu/46/edit', '/')) ->propertyAware('method', 'GET') ->matchRoute(); dump($route);
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。