控制器自动注入模型需参数名、路由变量名、模型路由键三者严格对齐,否则退化为字符串;隐式绑定生效须同时满足:路由参数名与变量名一致、类继承Eloquent Model、路由键可被参数值直接查询。

控制器自动注入模型不是“配了就能用”,而是依赖参数名、路由变量名、模型路由键三者严格对齐。不匹配就退化成普通字符串参数,不会报错但查不到数据。
隐式绑定生效的三个硬性条件
隐式绑定只在同时满足以下三点时才触发:
-
{user}这个路由参数名,必须和类型提示的变量名完全一致(function (User $user)中的$user) - 被提示的类必须是继承自
Illuminate\Database\Eloquent\Model的 Eloquent 模型 - 该模型的路由键(
getRouteKey()返回值)必须能被路由参数值直接查询,比如默认是id字段,那{user}就得传数字 ID;如果模型重写了getRouteKey()返回slug,那{user}就得传 slug 字符串
常见错误:写 Route::get('/users/{id}', function (User $user) { ... }) —— 参数名是 {id},但变量名是 $user,不匹配,$user 会是 null 或抛出 Target class [User] does not exist(如果没 use)。
显式绑定用于非 ID 字段或复杂逻辑
当你要用 slug、uuid 或组合条件(如 status = 'published')查模型时,必须用 Route::bind() 或 Route::model() 显式注册:
在 app/Providers/RouteServiceProvider.php 的 boot() 方法里加:
Route::bind('post', function ($value) {
return \App\Models\Post::where('slug', $value)
->where('status', 'published')
->firstOrFail();
});
然后路由写成 Route::get('/posts/{post}', [...]),此时 {post} 传的是 slug 值,$post 就是查出来的模型实例。
注意:Route::model('post', Post::class) 只支持按主键查,不能加 where 条件;要加条件必须用 Route::bind()。
模型里自定义路由行为的关键方法
如果不想全局改绑定逻辑,可以在模型内部控制解析方式:
- 重写
getRouteKey():决定用哪个字段当路由键(返回'slug'后,{post}就自动按 slug 查) - 重写
resolveRouteBinding($value, $field = null):接管整个查找逻辑,比如支持按id或slug任一方式匹配 - 确保模型用了
Illuminate\Database\Eloquent\Concerns\HasRouteKeytrait(Laravel 10 默认已用)
例如:
public function resolveRouteBinding($value, $field = null)
{
return $this->where('slug', $value)->orWhere('id', $value)->firstOrFail();
}
这样访问 /posts/1 或 /posts/my-post 都能命中同一个模型。
调试注入失败最有效的两步
当模型没注入成功,别急着翻文档,先做这两件事:
- 在控制器方法里加
dd(gettype($user), $user),看是object还是string—— 是 string 就说明隐式绑定根本没触发 - 检查路由缓存是否过期:
php artisan route:clear,尤其改过RouteServiceProvider后,缓存不清理会导致新绑定逻辑不生效
最容易被忽略的是:模型类没正确 use,或者命名空间拼错,这时候类型提示失效,框架就当它是个普通参数,连数据库都不会碰。


















