
本文介绍如何在 Laravel 中利用 Eloquent 的 whereHas 方法,结合多对多关系或自定义中间表关系,精准获取属于某分类且 featured = 1 的视频列表,避免手动循环、空值插入及 N+1 查询问题。
本文介绍如何在 laravel 中利用 eloquent 的 `wherehas` 方法,结合多对多关系或自定义中间表关系,精准获取属于某分类且 `featured = 1` 的视频列表,避免手动循环、空值插入及 n+1 查询问题。
在 Laravel 应用中处理多对多关系(如 Videos ↔ Categories)时,若需基于中间表(如 category_video)附加条件(例如仅获取 featured = 1 的视频),直接使用 pluck() + 循环 + first() 不仅低效,还会导致结果数组中混入 null 值,破坏数据一致性与后续处理逻辑。
✅ 推荐方案:使用 whereHas() 配合关系预定义
首先,在 Video 模型中正确定义与 Category 的多对多关系(推荐方式):
// app/Models/Video.php
public function categories()
{
return $this->belongsToMany(Category::class, 'category_video', 'video_id', 'category_id');
}
然后,一行代码即可获取指定分类下所有「已精选」的视频:
$featuredVideos = Video::where('featured', 1)
->whereHas('categories', function ($query) use ($category_id) {
$query->where('id', $category_id);
})
->get();
该查询生成一条高效的 SQL(含 EXISTS 子查询),仅返回同时满足「视频 featured=1」且「归属指定 category_id」的完整 Video 实例,零空值、零循环、无 N+1 问题。
? 进阶优化:若需直接关联中间表字段(如排序、时间戳等)
若 category_video 表包含额外字段(如 created_at、sort_order),建议补充中间表模型 CategoryVideo 并建立反向关系:
// app/Models/Video.php
public function categoryVideo()
{
return $this->hasMany(CategoryVideo::class, 'video_id');
}
对应地,可改用中间表关系筛选(语义更明确,尤其当条件涉及中间表字段时):
$featuredVideos = Video::where('featured', 1)
->whereHas('categoryVideo', function ($query) use ($category_id) {
$query->where('category_id', $category_id);
})
->get();
⚠️ 注意事项:
- 确保数据库中
category_video表主键/外键命名与belongsToMany()参数一致(默认为video_id/category_id); -
whereHas()默认使用EXISTS,性能优异;若需获取中间表数据(如pivot字段),应使用withPivot()+with()或join(); - 避免在循环中执行多次数据库查询(即“N+1 问题”),本方案全程单次查询完成;
- 若需分页,将
get()替换为paginate()即可无缝支持。
通过合理建模与 whereHas 的组合使用,你不仅能写出更简洁、可读性更强的代码,更能保障应用在数据量增长时的查询性能与稳定性。











