You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 17:15:55