路由中间件之 SubstituteBindings

简介

[
    'App\Http\Middleware\EncryptCookies',
    'Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse',
    'Illuminate\Session\Middleware\StartSession',
    'Illuminate\View\Middleware\ShareErrorsFromSession',
    'App\Http\Middleware\VerifyCsrfToken',
    'Illuminate\Routing\Middleware\SubstituteBindings',  // 本章内容
    'App\Http\Middleware\RedirectIfAuthenticated',
]

SubstituteBindings 作用:显式和隐式地根据请求参数绑定对应数据模型。

这样说,可能大家有点思维模糊。我举一个控制器方法大家就知道了

路由

Route::put('user/{user}', 'UserController@update');

控制器

public function update(User $user, Request $request)
{
    // 
}

如上,当 PUT 请求 http://localhost/user/1,实现用户信息修改时。将自动寻找 ID 为 1 的 User 模型,绑定到控制器的 update 方法上。

显式绑定的源码原理

SubstituteBindings 中间件的 handle 方法

public function handle($request, Closure $next)
{
    // 显式绑定
    $this->router->substituteBindings($route = $request->route());

    $this->router->substituteImplicitBindings($route);

    return $next($request);
}

我们来看一下路由的 substituteBindings 方法:

public function substituteBindings($route)
{
    // $route->parameters() 返回的是请求的参数
    foreach ($route->parameters() as $key => $value) {
        if (isset($this->binders[$key])) {
            $route->setParameter($key, $this->performBinding($key, $value, $route));
        }
    }

    return $route;
}

$this->binders 储存的数据格式是 'string' => '闭包'string 就是请求参数中 name,而 闭包 的参数是请求里面的对应 value,返回的是处理后的 value。一般是返回寻找到模型。原因我们来看一下 $this->performBinding 方法

protected function performBinding($key, $value, $route)
{
    // PHP 的闭包调用。传入参数值和路由
    return call_user_func($this->binders[$key], $value, $route);
}

那么 $this->binders 如何被赋值的呢?

public function bind($key, $binder)
{
    $this->binders[str_replace('-', '_', $key)] = RouteBinding::forCallback(
        $this->container, $binder
    );
}

如何调用,什么时候调用的 bind 方法,我们来看官方文档

Laravel

文档位置传送门

Route::bind('user', function ($value) {
    return App\User::where('name', $value)->first() ?? abort(404);
});

自定义逻辑解析:就是调用 bind 方法的时刻与位置。

上面这段代码的作用:如下设定

路由

Route::put('user/{user}', 'UserController@update');

控制器

public function update(User $user, Request $request)
{
    //...
}

当请求URL:http://localhost/user/yuanshang 时,将在 users 表中寻找 name 字段为 yuanshang 的数据模型,然后重新绑定到 user 参数上,然后传递到控制器上。此时 $user 就是 name 等于 yuanshang 的模型参数数据。

隐式绑定的源码原理

SubstituteBindings 中间件的 handle 方法

public function handle($request, Closure $next)
{    
    $this->router->substituteBindings($route = $request->route());

    // 隐式绑定
    $this->router->substituteImplicitBindings($route);

    return $next($request);
}

我们来看一下路由的 substituteImplicitBindings 方法:

public function substituteImplicitBindings($route)
{
    ImplicitRouteBinding::resolveForRoute($this->container, $route);
}

继续查看 resolveForRoute 方法,传入 Laravel 容器和匹配路由对象

public static function resolveForRoute($container, $route)
{
    // 获取原生的请求参数对
    $parameters = $route->parameters();

    // 获取路由定义的可变参数列表
    foreach ($route->signatureParameters(UrlRoutable::class) as $parameter) {
        // 如果请求的参数名称没有在路由定义的可变参数列表中,则跳过循环
        if (! $parameterName = static::getParameterName($parameter->name, $parameters)) {
            continue;
        }

        // 获取对应值
        $parameterValue = $parameters[$parameterName];

        // 如果值是对象且属于 UrlRoutable 类,跳过循环
        if ($parameterValue instanceof UrlRoutable) {
            continue;
        }

        // 获取模型实例
        $instance = $container->make($parameter->getClass()->name);

        // 根据定义的字段获取对应数据模型
        if (! $model = $instance->resolveRouteBinding($parameterValue)) {
            throw (new ModelNotFoundException)->setModel(get_class($instance));
        }

        // 将模型重新绑定到路由参数上
        $route->setParameter($parameterName, $model);
    }
}

最后讲一下

Laravel 的资源控制器:

Laravel

上面 {photo} 就是 photos 数据表的 ID,在控制器中即可以用数据模型绑定的方法,便捷操作数据。

public function update(Photo $photo, Request $request)
{
    $photo->name = '修改过的名称';
    $photo->save();

    return response()->json([
        'code' => 200,
        'message' => '修改成功',
    ]);
}
本篇如有错误、不当或者需补充的内容,请各位同僚多提宝贵意见。

本文章首发在 LearnKu.com 网站上。
上一篇 下一篇
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
讨论数量: 0
发起讨论 只看当前版本


暂无话题~