详情页用show()查单条数据最稳,优先路由模型绑定或findorfail(),预加载关联防n+1,slug/uuid需显式查询,软删除和权限校验需协同处理。

用 show() 方法查单条数据,别手写 SQL
控制器里处理详情页,核心就是查一条记录并渲染视图。Laravel 默认路由模型绑定和 show() 方法已经铺好路,直接用 Model::findOrFail($id) 最稳——它会在查不到时抛 ModelNotFoundException,自动转成 404 响应,不用自己 if (! $model) abort(404)。
- 别在
show()里手动写DB::table('posts')->where('id', $id)->first():丢失模型事件、访问器、强制类型转换等好处 - 如果 URL 是
/posts/{post}(隐式绑定),Laravel 自动注入已查询的$post实例,连findOrFail都省了 - 显式绑定或自定义查找逻辑(比如按 slug)?在
routes/web.php里用Route::bind('post', function ($value) { return Post::where('slug', $value)->firstOrFail(); });
关联数据用 with() 预加载,别在 Blade 里 $post->author->name
详情页常要显示作者、分类、标签等关联字段。如果模板里直接点调用,每查一个关联就触发一次 SQL 查询(N+1 问题),10 条评论 + 1 个作者 + 3 个标签 = 至少 15 次查询。
- 在控制器里统一预加载:
$post = Post::with(['author', 'category', 'tags'])->findOrFail($id); - 深层嵌套也支持:
with(['comments.user', 'comments.replies.user']),但注意别过度加载,只选页面真要用的字段 - 如果某些关联仅在特定条件下才需要(比如管理员看隐藏评论),用
load()延迟加载:$post->load('hiddenComments');
URL 参数校验别漏掉 slug 或 uuid 场景
用 ID 做路由参数最简单,但暴露自增主键、易被遍历。换成 slug 或 uuid 更安全,这时控制器不能只依赖 findOrFail 的默认行为。
-
slug:确保数据库字段有唯一索引,并在查询时明确指定字段:Post::where('slug', $slug)->firstOrFail(),别用findOrFail($slug)(它默认查id) -
uuid:记得用whereUuid()(Laravel 9+)或where('uuid', $uuid),别传进findOrFail()——它会当整数处理,导致查不到 - 如果用了软删除,且详情页需展示已删除内容(如后台预览),加
withTrashed();否则默认不查,这是预期行为,不是 bug
返回前检查权限,别把 authorize() 写在中间件里硬塞
详情页常涉及权限控制:比如私密文章只对作者/管理员可见。用策略类(Policy)配合 authorize() 最干净,但位置很关键。
- 在
show()开头立即调用:$this->authorize('view', $post);,而不是放到方法末尾或单独中间件——否则可能白查一次数据库 - 策略方法里别重复查数据:
public function view(User $user, Post $post)中的$post已是实例,直接用属性判断即可 - 如果策略逻辑复杂(比如要查用户订阅状态),在策略里用缓存或延迟加载,别让
authorize()变成性能瓶颈
with() 默认不包含软删除关联,而策略里 $post->trashed() 是可用的——但如果你在策略里又去查了一遍 $post->fresh(),就绕过了控制器里的预加载,多了一次查询。











