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

如何使用Laravel Eloquent获取最近下单的唯一商品?

使用Eloquent筛选最近下单的5个唯一商品

我有以下数据库表和Eloquent模型(已简化):

  • order_items 表,对应模型 OrderItem::class
  • products 表,对应模型 Product::class

order_items 表中包含多态关联字段 orderable_id 和 orderable_type,单个商品可被多次下单。数据库中的最新记录如下:

idorderable_typeorderable_idcreated_at
6App\Product2452022-09-01 17:00
5App\Product5122022-08-29 12:15
4App\Product2452022-08-28 19:30
3App\Product4112022-08-28 11:50
2App\Product2512022-08-27 10:10
1App\Product1232022-08-26 14:20

需求

需要筛选出最近下单的5个唯一商品(重复的商品ID仅保留一次),正确结果应为:

245
512
411
251
123

当前问题

由于数据量达数千条,无法一次性取出所有订单项。当前使用的查询会因为热门商品重复下单,导致结果全是同一个商品:

id.orderable_typeorderable_idcreated_at
24566App\Product5102022-09-01 17:00
24565App\Product5102022-08-29 12:15
24564App\Product5102022-08-28 19:30
24563App\Product5102022-08-28 11:50
24562App\Product5102022-08-27 10:10
24561App\Product5102022-08-26 14:20

当前使用的查询语句:

OrderItem::whereHasMorph('orderable', [Product::class])
    ->orderBy('created_at', 'desc')
    ->take(5)
    ->get()
    ->pluck('orderable');

另外,MySQL启用了严格模式,直接使用groupBy()会报错。

解决方案

方法1:子查询获取每个商品的最新下单记录

通过子查询先获取每个orderable_id对应的最新created_at,再基于这个结果筛选并排序,最后取前5个:

$latestOrderItems = OrderItem::select('orderable_id', \DB::raw('MAX(created_at) as latest_created'))
    ->whereHasMorph('orderable', [Product::class])
    ->groupBy('orderable_id')
    ->orderBy('latest_created', 'desc')
    ->take(5)
    ->get();

// 获取对应的商品模型并保持排序
$productIds = $latestOrderItems->pluck('orderable_id');
$products = Product::whereIn('id', $productIds)
    ->orderByRaw("FIELD(id, {$productIds->implode(',')})")
    ->get();

方法2:使用窗口函数(MySQL 8.0+)

利用窗口函数ROW_NUMBER()为每个商品的下单记录排序,只保留每个商品的最新一条,再取前5个:

$products = OrderItem::whereHasMorph('orderable', [Product::class])
    ->selectRaw('*, ROW_NUMBER() OVER (PARTITION BY orderable_id ORDER BY created_at DESC) as rn')
    ->having('rn', '=', 1)
    ->orderBy('created_at', 'desc')
    ->take(5)
    ->get()
    ->pluck('orderable');

方法3:合规使用groupBy适配严格模式

在严格模式下,只要select中的字段都在groupBy中,或使用聚合函数,就可以正常使用groupBy:

$productIds = OrderItem::whereHasMorph('orderable', [Product::class])
    ->select('orderable_id')
    ->groupBy('orderable_id')
    ->orderByRaw('MAX(created_at) desc')
    ->take(5)
    ->pluck('orderable_id');

$products = Product::whereIn('id', $productIds)
    ->orderByRaw("FIELD(id, {$productIds->implode(',')})")
    ->get();

以上方法均无需加载全量数据,可高效获取最近下单的5个唯一商品。


内容的提问来源于stack exchange,提问作者The_JohnDavies

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 07:33:53