laravel 中根据请求路径、方法匹配路由实例

需求

最近由于业务功能的需求,需要根据数据库记录的请求路径(如admin/auth/menu/46/edit)、请求方法(如GET)去匹配路由实例,拿到路由实例后续做一些其他事情。

分析

其实就是路由的核心功能(将一类请求映射匹配到一个回调类型的变量)。框架本身自带的功能,查找源码是以下代码块实现的:

// 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;
}

代码大概逻辑是:遍历路由集合,将当前请求丢给四个验证器 UriValidatorMethodValidatorSchemeValidatorHostValidator 去逐一验证匹配,验证器主要去验证匹配请求对象的 pathInfomethodschemehost 信息。

所以只需要修改当前的请求对象的这些信息,丢给上面的 matches 方法就可以实现我需要的功能了。

实现

由于是同一个项目,所以 schemehost 与当前请求一致无需修改。而 pathInfomethod 是两个私有属性以及没有找到对应写权限的方法。所以实现一个有能力写属私性的宏方法即可。最终代码如下:

<?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);
            });
        };
    }
}

注册请求宏

// App\Providers\AppServiceProvider
public function register()
{
    Request::mixin($this->app->make(RequestMacro::class));
}

使用示例

$route = request()
    ->propertyAware('pathInfo', \Illuminate\Support\Str::start('admin/auth/menu/46/edit', '/'))
    ->propertyAware('method', 'GET')
    ->matchRoute();

dump($route);

原文链接

本作品采用《CC 协议》,转载必须注明作者和本文链接
No practice, no gain in one's wit. 我的 Gitub
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!
未填写
文章
42
粉丝
107
喜欢
884
收藏
1252
排名:46
访问:14.4 万
私信
所有博文