
本文介绍如何在 Laravel 中利用 Eloquent 关系与 whereHas 方法,精准获取指定分类下 featured = 1 的所有视频,避免手动遍历、空值插入及 N+1 查询问题。
本文介绍如何在 laravel 中利用 eloquent 关系与 `wherehas` 方法,精准获取指定分类下 `featured = 1` 的所有视频,避免手动遍历、空值插入及 n+1 查询问题。
在 Laravel 开发中,处理多对多关系(如视频与分类)时,若需基于中间表(pivot)加条件筛选(例如仅获取“精选”视频),直接使用 pluck() + 循环查询不仅性能低下,还会因 first() 返回 null 导致结果数组中混入空元素,破坏数据结构一致性。
✅ 推荐方案:使用 whereHas() 配合正向关联关系
首先,确保 Video 模型正确定义了与 Category 的多对多关系(通过 category_video 中间表):
// app/Models/Video.php
public function categories()
{
return $this->belongsToMany(Category::class, 'category_video');
}随后,一行代码即可获取指定分类 ID 下所有 featured = 1 的视频集合:
$featuredVideos = Video::where('featured', 1)
->whereHas('categories', function ($query) use ($category_id) {
$query->where('id', $category_id);
})
->get();该查询生成一条高效的 SQL(含 EXISTS 子查询),仅扫描必要记录,且返回的是纯净的 Video 实例集合,无 null 元素。
? 进阶优化:直接关联 pivot 表(适用于复杂中间表逻辑)
若中间表 category_video 自身包含额外字段(如 sort_order、is_approved),或你更倾向显式操作 pivot 模型,可定义 hasMany 关系:
// app/Models/Video.php
public function categoryVideo()
{
return $this->hasMany(CategoryVideo::class, 'video_id', 'id');
}然后通过中间表反向筛选:
$featuredVideos = Video::where('featured', 1)
->whereHas('categoryVideo', function ($query) use ($category_id) {
$query->where('category_id', $category_id);
})
->get();⚠️ 注意事项
- 确保数据库中
category_video表存在复合索引:INDEX(category_id, video_id),以加速whereHas查询; - 避免在循环中执行
Video::find()或where(...)->first()—— 这将触发 N+1 查询,严重拖慢响应; - 若需同时加载分类信息(如显示分类名称),可链式调用
with('categories')实现预加载; -
whereHas默认要求至少一个匹配关联,如需包含无关联但满足其他条件的记录,请改用whereDoesntHave或withCount配合having。
通过合理建模与原生关系方法,你不仅能写出更简洁、可读性更强的代码,更能获得显著的性能提升与稳定的输出结构。


















