如何使用Laravel Eloquent获取最近下单的唯一商品?
使用Eloquent筛选最近下单的5个唯一商品
我有以下数据库表和Eloquent模型(已简化):
order_items表,对应模型OrderItem::classproducts表,对应模型Product::class
order_items 表中包含多态关联字段 orderable_id 和 orderable_type,单个商品可被多次下单。数据库中的最新记录如下:
| id | orderable_type | orderable_id | created_at |
|---|---|---|---|
| 6 | App\Product | 245 | 2022-09-01 17:00 |
| 5 | App\Product | 512 | 2022-08-29 12:15 |
| 4 | App\Product | 245 | 2022-08-28 19:30 |
| 3 | App\Product | 411 | 2022-08-28 11:50 |
| 2 | App\Product | 251 | 2022-08-27 10:10 |
| 1 | App\Product | 123 | 2022-08-26 14:20 |
需求
需要筛选出最近下单的5个唯一商品(重复的商品ID仅保留一次),正确结果应为:
245 512 411 251 123
当前问题
由于数据量达数千条,无法一次性取出所有订单项。当前使用的查询会因为热门商品重复下单,导致结果全是同一个商品:
| id. | orderable_type | orderable_id | created_at |
|---|---|---|---|
| 24566 | App\Product | 510 | 2022-09-01 17:00 |
| 24565 | App\Product | 510 | 2022-08-29 12:15 |
| 24564 | App\Product | 510 | 2022-08-28 19:30 |
| 24563 | App\Product | 510 | 2022-08-28 11:50 |
| 24562 | App\Product | 510 | 2022-08-27 10:10 |
| 24561 | App\Product | 510 | 2022-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
相关产品推荐
相关产品推荐

