本节摘要:路由模型绑定让 URL 里的编号自动变成 Eloquent 模型对象:控制器方法签名里写好类型,框架替你查库、替你 404。本节讲隐式绑定的约定与键定制、显式绑定的场景化写法、软删除与无效编号的处理,以及绑定时顺手做权限过滤的思路。读完你能删掉项目里成片的"查一下、判空、404"三连代码。
上一节的约束只保证"编号长得像编号",控制器拿到的仍是一串数字,还得自己查库、自己判空:
<?php // 没有绑定的老写法:每个动作都重复这三行 public function show(int $id) { $order = Order::find($id); // 查 if (! $order) { // 判 abort(404); // 兜 } return view('orders.show', ['order' => $order]); }
十个动作就是三十行样板代码。路由模型绑定把这坨胶水整体收走:参数名对上类型名,框架替你完成"查与兜"。
<?php // 路由侧:参数名 {order} 即可 Route::get('/orders/{order}', [OrderController::class, 'show']); // 控制器侧:类型写明 Order,参数名叫 $order,绑定自动发生 public function show(Order $order) { // 到手就是模型实例;查无此单时框架已抛 404 return view('orders.show', ['order' => $order]); }
生效条件就两条:参数名与变量名一致、类型是 Eloquent 模型。想按编号以外的字段找单,最常见是 slug:
<?php // 全局改键:模型里重写 getRouteKeyName public function getRouteKeyName(): string { return 'slug'; // URL 用 /orders/laravel-guide,按 slug 字段查 } // 单条路由临时改键:用冒号语法 Route::get('/posts/{post:slug}', [PostController::class, 'show']);
⚠️ 常见坑:绑定的查询默认不带软删除过滤的例外——软删除的记录用默认绑定查不到,会直接 404。若编辑入口需要找回已软删的记录,用下文的显式绑定配合 withTrashed。另外绑定失败抛的是 ModelNotFoundException,框架自动转 404 响应,你不需要自己 catch。
隐式绑定靠参数名匹配,有几个场景它不够用:路由参数名与模型类对不上号、想复用一条自定义查询逻辑、软删除找回。显式绑定把这些规则集中登记:
<?php // AppServiceProvider 的 boot 方法里登记 use App\Models\Order; use Illuminate\Support\Facades\Route; public function boot(): void { // {worker_order} 这种不规则参数名,指明它对应 Order 模型 Route::model('worker_order', Order::class); // 或者完全接管解析逻辑:自定义"怎么查" Route::bind('order', function (string $value) { return Order::where('sn', $value) ->orWhere('id', $value) ->withTrashed() // 连软删除的一并找回 ->firstOrFail(); }); }

模型绑定最大的红利不只是省三行代码,而是"解析即过滤"。工单系统里,编号连续意味着别人猜得到别人的单号,光绑定不筛归属就是越权漏洞的温床。在 bind 闭包里把当前用户过滤加上,越权编号直接 404:
<?php Route::bind('order', function (string $value) { // 登录用户只能解析到自己团队的单子;游客身份这里拿不到用户则统一 404 return Order::where('team_id', auth()->user()?->team_id) ->where(function ($q) use ($value) { $q->where('sn', $value)->orWhere('id', $value); }) ->firstOrFail(); });
这个写法把"归属检查"从每个控制器动作里抽走,集中在一处。第七章讲授权策略时,会把这套思路升级成声明式的 Policy,两相配合后,越权这类问题在进件口就断了根。
绑定和软删除怎么配合?
默认绑定只查未删除的记录,软删的单子一访问就 404。需要找回的场景(回收站、恢复入口)用显式绑定加 withTrashed;而"已删除就该当不存在"的常规页面保持默认即可——两种诉求别混在一条路由里。
一个路由里有多个可绑定参数怎么办?
按顺序各自解析:orders 路径加 teams 路径的嵌套路由,两个参数分别按类型与参数名各自绑定,互不干扰。唯一要留意的是嵌套资源做归属校验(这张单属于这个队)不能依赖绑定自动完成,得在 Policy 或绑定闭包里显式比对。
测试里怎么直接拿绑定的模型?
不需要特殊手段:Feature 测试发请求时把工厂造出的模型编号放进 URI,绑定自然生效;用不存在的编号发请求,断言响应是 404,这本身就是一条值得写的验收用例(第九章会落在纸上)。
参数换成实物了,进件口的最后一道手续是安检:下一节讲中间件管道怎么设卡,以及怎么培养自己的门卫。