
本文介绍如何将三种不同优先级的广告(钻石、VIP、普通)查询合并为单个 Eloquent 查询,并通过 paginate() 实现高效、可排序的分页展示,避免多次数据库查询与内存拼接。
本文介绍如何将三种不同优先级的广告(钻石、vip、普通)查询合并为单个 eloquent 查询,并通过 `paginate()` 实现高效、可排序的分页展示,避免多次数据库查询与内存拼接。
在 Laravel 开发中,常见业务场景如分类广告列表需按“钻石广告 → VIP 广告 → 普通广告”优先级排序展示,且要求整体分页(而非每类单独分页)。原始写法中分别执行三次 get() 查询,不仅增加数据库压力,还导致无法跨类型统一分页、排序逻辑分散、内存占用高(尤其数据量大时),且后续 foreach 遍历需手动合并集合,丧失分页对象(LengthAwarePaginator)提供的 links()、hasPages() 等便利功能。
✅ 正确解法是使用 Eloquent 的闭包作用域 + 条件分组 + paginate(),将三类广告逻辑统一收束到单条 SQL 查询中:
$data['ads'] = AdModel::where('status', 1)
->where('moderation', 0)
->where('category_id', $this_category_id)
->where(function ($query) {
// 三类广告的 OR 关系:diamond=1 || (vip=1 AND diamond=0) || (vip=0 AND diamond=0)
$query->where('ad_diamond', 1)
->orWhere(function ($q) {
$q->where('ad_vip', 1)
->where('ad_diamond', 0);
})
->orWhere(function ($q) {
$q->where('ad_vip', 0)
->where('ad_diamond', 0);
});
})
->orderByDesc('date_added')
->paginate(20); // 每页20条,支持 ?page=2 自动解析
⚠️ 注意事项:
-
排序稳定性:上述查询仅按
date_added排序,若需严格保持“钻石 > VIP > 普通”的层级优先级(例如同一天添加时钻石广告始终置顶),应改用orderByRaw添加权重字段:->orderByRaw("CASE WHEN ad_diamond = 1 THEN 1 WHEN ad_vip = 1 THEN 2 ELSE 3 END") ->orderByDesc('date_added') -
索引优化:为保障性能,建议在数据库中为
(status, moderation, category_id, ad_diamond, ad_vip, date_added)建立复合索引。 -
空结果处理:
paginate()返回的是LengthAwarePaginator实例,视图中可直接使用$ads->links()渲染分页导航,无需额外判空。 -
避免 N+1:若广告关联了图片、用户等模型,请务必使用
with()预加载,例如->with(['user', 'images'])。
最终在 Blade 模板中遍历即可:
@foreach($ads as $ad)
<div class="ad-card {{ $ad->ad_diamond ? 'diamond' : ($ad->ad_vip ? 'vip' : 'default') }}">
<h3>{{ $ad->title }}</h3>
<span>发布时间:{{ $ad->date_added }}</span>
</div>
@endforeach
{{ $ads->links() }}
该方案兼顾性能、可维护性与 Laravel 最佳实践,是处理多条件分层数据分页的标准推荐方式。











