Laravel分页分组去重:确保结果数量一致的技术方案求助
解决方案:Laravel商品分页去重并关联重复项
核心思路
先基于筛选后的结果按product_type和website.domain_id分组,获取每个分组的主商品ID;再对这些主商品ID进行分页,保证每页数量准确;最后批量加载每个主商品对应的重复项(同样符合筛选条件)。
分步实现
1. 构建基础筛选查询
先定义包含所有用户筛选、排序、关联网站的基础查询,后续所有操作都基于这个查询,确保筛选逻辑一致:
// 基础查询:包含筛选、排序、关联网站,获取商品及对应域名ID $baseQuery = Product::query() ->select(['products.*', 'websites.domain_id']) ->where('products.active', true) ->joinWebsites() ->filter($request->filters) ->sort();
2. 获取唯一分组的主商品ID
从基础查询中按product_type和domain_id分组,选择每个分组的主商品(这里以价格最低为例,可根据排序需求调整):
// 获取每个唯一组的主商品ID:分组后取价格最低的商品 $mainProductIds = $baseQuery->clone() ->select( 'product_type', 'websites.domain_id', DB::raw('SUBSTRING_INDEX(GROUP_CONCAT(products.id ORDER BY products.price ASC), ",", 1) as main_product_id') ) ->groupBy('product_type', 'websites.domain_id') ->pluck('main_product_id');
若需按其他规则选主商品(如最新创建),只需修改
GROUP_CONCAT中的ORDER BY字段,比如ORDER BY products.created_at DESC。
3. 分页主商品并加载关联
用获取到的主商品ID进行分页,同时加载所需的关联关系:
// 分页主商品,保持和基础查询一致的排序规则 $paginatedProducts = Product::query() ->with([ 'niches', 'currency', 'website', 'flags', 'account', 'languages', 'categories', 'productFavorites' => function ($query) { $query->where('account_id', Auth::user()->selected_account_id); }, ]) ->whereIn('id', $mainProductIds) ->orderByRaw($baseQuery->getQuery()->orders[0]->sql) // 复用基础查询的排序 ->paginate(20); // 替换为你的每页数量
4. 批量加载重复项(性能优化版)
一次性查询所有主商品对应的重复项,再分配到对应主商品上,避免N次数据库请求:
// 收集主商品的分组标识(type_domainId)与ID的映射 $mainProductGroups = $paginatedProducts->getCollection()->mapWithKeys(function (Product $product) { return ["{$product->product_type}_{$product->website->domain_id}" => $product->id]; }); // 一次性查询所有符合条件的重复项 $allDuplicates = $baseQuery->clone() ->where(function ($query) use ($mainProductGroups) { foreach ($mainProductGroups as $groupKey => $mainId) { [$type, $domainId] = explode('_', $groupKey); $query->orWhere(function ($q) use ($type, $domainId, $mainId) { $q->where('product_type', $type) ->where('websites.domain_id', $domainId) ->where('products.id', '!=', $mainId); }); } }) ->get(); // 将重复项分组后分配到对应主商品 $allDuplicates->groupBy(function ($product) { return "{$product->product_type}_{$product->website->domain_id}"; })->each(function ($duplicates, $groupKey) use ($paginatedProducts, $mainProductGroups) { $mainProduct = $paginatedProducts->getCollection()->firstWhere('id', $mainProductGroups[$groupKey]); if ($mainProduct) { $mainProduct->setAttribute('otherOffers', $duplicates); } });
方案优势
- 分页数量准确:先获取所有唯一主商品ID再分页,确保每页返回设定数量的唯一结果。
- 筛选逻辑完整:所有主商品和重复项都基于用户的筛选条件,不会遗漏符合要求的商品。
- 性能高效:通过复用基础查询、批量获取重复项,将数据库请求次数降至最低(仅3次:分组查询、分页查询、重复项查询)。
内容的提问来源于stack exchange,提问作者Lachezar Uzunov
相关产品推荐
相关产品推荐

