Laravel 5.2 如何定义带有可选的隐式模型绑定参数的路由

Laravel 5.2 增加了一个很方便的特性:隐式模型绑定 (Implicit Model Binding) ,可以直接将 URL 中的 id 对应到相应的 model 。比如希望访问 profile/12345 返回 12345 这个 id 对应的用户的资料,我们可以这样定义路由:

Route::get('profile/{user}', function (User $user) {
    return $user;
});

同时 Laravel 路由也支持可选参数,比如:

Route::get('user/{name?}', function ($name = null) {
   //
});

那么如果把这两者结合起来呢?比如访问 profile/12345 是 userID 12345 这个用户的资料,访问 profile 是当前登录用户的资料。我想的「可选参数+隐式模型绑定」应该是酱紫:

Route::get('profile/{user?}', function (User $user = null) {
    return $user ?: Auth::user();
});

看起来很优雅。但是,这样写没有获得想要的效果。不指定 {user} 参数或其对应的用户不存在时,没抛404异常,$user 也不是 null$user 是一个新创建的(空的,数据库里不存在的)User 对象。

所以我们还要加个判断:

Route::get('profile/{user?}', function (User $user = null) {
    if (is_null($user) || ! $user->exists) {
        $user = Auth::user();
    }

    return $user;
});

如果类似的路由有很多,岂不是很麻烦?有个更好的解决方案是使用「显式绑定」。

RouteServiceProviderboot 方法中绑定 userUser 类:

Route::model('user', \App\User::class);

然后在处理方法里移除隐式绑定就可以了:

Route::get('profile/{user?}', function ($user = null) {
    return $user ?: Auth::user();
});

这样完成很好,访问 profile 返回当前登录用户的资料, 访问 profile/123 返回 userID 123 的资料,访问 profile/foo 返回 404 (ModelNotFoundException) 。但是这其实是 Laravel 5.2 之前的写法,没有用到「模型绑定」的特性。

所以有没有更优雅的解决方案?或者说如何扩展 Laravel 使得不需要加判断也不需要定义显式绑定就可以支持「带可选参数的隐式模型绑定」的路由?

相关 issue #13988

:point_right: Laravel 官网镜像 :cn:
本帖已被设为精华帖!
附言 1  ·  7年前

以 Laravel 5.3 为例,路由绑定是在 Illuminate\Routing\Middleware\SubstituteBindings 这个 middleware 中完成的, 它调用了 Router::substituteImplicitBindings 来替换路由参数中的 user 为 User 实例,如果对应的 userID 没有找到则设置路由参数 usernull

执行路由动作的 Route::run 中,会调用 resolveMethodDependencies 来实例化方法参数依赖对象,调用的是 container->make ,所以对于 function (User $user) 就 make 了一个新的对象。

本帖由 Summer 于 7年前 加精
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
讨论数量: 6

没人响应 :disappointed:

7年前 评论

:monkey_face: 我有在默默地看~ 看晕我了。 为什么要在路由里面做判断呢?

7年前 评论

@JokerLinly :smile: 既然有这个特性,就应该有它的用处。比如我文中提到的一个很常见的案例:

  • /profile 返回当前登录用户的资料
  • /profile/123 返回 userID 123 的资料
  • /profile/foo 返回 404 (ModelNotFoundException)

这三个都是展示用户资料页,route action 是相同的,所以只需要定义一个路由。另外,URL 也好看一点 :yum:

7年前 评论

如果模型主键不是'id',比如'uuid',要如何绑定呢?

5年前 评论

@Gabrielodbo

public function boot()
{
    parent::boot();

    Route::bind('user', function ($value) {
        return App\User::where('uuid', $value)->first() ?? abort(404);
    });
4年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!