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;
});
如果类似的路由有很多,岂不是很麻烦?有个更好的解决方案是使用「显式绑定」。
在 RouteServiceProvider
的 boot
方法中绑定 user
到 User
类:
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
以 Laravel 5.3 为例,路由绑定是在 Illuminate\Routing\Middleware\SubstituteBindings
这个 middleware 中完成的, 它调用了 Router::substituteImplicitBindings
来替换路由参数中的 user
为 User 实例,如果对应的 userID 没有找到则设置路由参数 user
为 null
。
执行路由动作的 Route::run
中,会调用 resolveMethodDependencies
来实例化方法参数依赖对象,调用的是 container->make
,所以对于 function (User $user)
就 make 了一个新的对象。
没人响应 :disappointed:
呵呵
:monkey_face: 我有在默默地看~ 看晕我了。 为什么要在路由里面做判断呢?
@JokerLinly :smile: 既然有这个特性,就应该有它的用处。比如我文中提到的一个很常见的案例:
/profile
返回当前登录用户的资料/profile/123
返回 userID 123 的资料/profile/foo
返回 404 (ModelNotFoundException)这三个都是展示用户资料页,route action 是相同的,所以只需要定义一个路由。另外,URL 也好看一点 :yum:
如果模型主键不是'id',比如'uuid',要如何绑定呢?
@Gabrielodbo