
在日常业务开发中,我们经常需要对数据进行复杂的聚合操作,例如在生成报告、配送单或统计报表时。laravel collection提供了强大且富有表现力的方法来处理这类需求。一个常见的场景是,当存在多批次相同规格的商品需要合并显示时,我们不仅要将它们分组,还要将它们的数量进行汇总,同时精简输出字段以符合最终展示要求。
例如,在圣诞树销售的配送单场景中,用户可能在不同托盘上装载了相同类型和尺寸的圣诞树。系统需要将这些重复的条目合并,并计算出该类型和尺寸的总数量,最终以清晰简洁的格式展示给客户。
原始数据通常以JSON格式存储,可能包含冗余信息,如slot、pallet、label等,而最终输出只需要type、size和amount(总和)。
原始数据示例 (line_items):
[
{ "slot": 2, "pallet": "cghjh", "type": "NGR", "label": "purple", "size": "125-150", "amount": "30" },
{ "slot": 3, "pallet": "cghjh", "type": "NGR", "label": "purple", "size": "125-150", "amount": "30" },
{ "slot": 2, "pallet": "yghiuj", "type": "NGR", "label": "orange", "size": "150-175", "amount": "30" },
{ "slot": 3, "pallet": "cghjh", "type": "NOB", "label": "purple", "size": "125-150", "amount": "30" }
]期望的输出格式:
{
"NGR": {
"125-150": [
{ "type": "NGR", "size": "125-150", "amount": 60 }
],
"150-175": [
{ "type": "NGR", "size": "150-175", "amount": 30 }
]
},
"NOB": {
"125-150": [
{ "type": "NOB", "size": "125-150", "amount": 30 }
]
}
}Laravel Collection的groupBy方法是实现数据聚合的第一步。我们可以根据一个或多个字段对Collection进行分组。在本例中,我们需要根据type(类型)和size(尺寸)进行分组。
$lineItems = collect([
[ "slot" => 2, "pallet" => "cghjh", "type" => "NGR", "label" => "purple", "size" => "125-150", "amount" => "30" ],
[ "slot" => 3, "pallet" => "cghjh", "type" => "NGR", "label" => "purple", "size" => "125-150", "amount" => "30" ],
[ "slot" => 2, "pallet" => "yghiuj", "type" => "NGR", "label" => "orange", "size" => "150-175", "amount" => "30" ],
[ "slot" => 3, "pallet" => "cghjh", "type" => "NOB", "label" => "purple", "size" => "125-150", "amount" => "30" ]
]);
$groupedData = $lineItems->groupBy(['type', 'size']);执行上述代码后,$groupedData的结构将如下所示:
{
"NGR": {
"125-150": [
{ "slot": 2, "pallet": "cghjh", "type": "NGR", "label": "purple", "size": "125-150", "amount": "30" },
{ "slot": 3, "pallet": "cghjh", "type": "NGR", "label": "purple", "size": "125-150", "amount": "30" }
],
"150-175": [
{ "slot": 2, "pallet": "yghiuj", "type": "NGR", "label": "orange", "size": "150-175", "amount": "30" }
]
},
"NOB": {
"125-150": [
{ "slot": 3, "pallet": "cghjh", "type": "NOB", "label": "purple", "size": "125-150", "amount": "30" }
]
}
}可以看到,groupBy已经成功地按照type和size将数据进行了两级分组。然而,它只是简单地将原始条目归类,并未进行数量求和,也没有过滤掉不需要的字段。每个size组下仍然是一个包含多个原始条目的Collection。
如果尝试在groupBy之后直接使用map来求和,例如:
$data = collect($deliveryNote->line_items)
->groupBy(['type', 'size'])
->map(function ($item) {
return $item->sum('amount');
});这并不能得到期望的结果。原因在于,外层的map回调函数接收到的$item是按type分组后的Collection(例如,"NGR"下的所有size分组),而不是单个的size分组下的原始条目Collection。要对每个type和size组合下的amount进行求和,我们需要进行更深层次的迭代。
为了达到期望的输出格式,我们需要在groupBy之后进行两层map操作。外层map处理type分组,内层map处理size分组,并在最内层执行求和与字段筛选。
<?php
use Illuminate\Support\Collection;
// 模拟 $deliveryNote->line_items 数据
$lineItems = collect([
[ "slot" => 2, "pallet" => "cghjh", "type" => "NGR", "label" => "purple", "size" => "125-150", "amount" => "30" ],
[ "slot" => 3, "pallet" => "cghjh", "type" => "NGR", "label" => "purple", "size" => "125-150", "amount" => "30" ],
[ "slot" => 2, "pallet" => "yghiuj", "type" => "NGR", "label" => "orange", "size" => "150-175", "amount" => "30" ],
[ "slot" => 3, "pallet" => "cghjh", "type" => "NOB", "label" => "purple", "size" => "125-150", "amount" => "30" ]
]);
$processedData = $lineItems
->groupBy(['type', 'size']) // 第一步:按 'type' 和 'size' 进行多级分组
->map(function (Collection $sizeGroups, string $type) { // 第二步:遍历每个 'type' 组
// $sizeGroups 是一个 Collection,其键是 'size',值是对应 'size' 下的原始条目 Collection
return $sizeGroups->map(function (Collection $itemsInSizeGroup, string $size) { // 第三步:遍历每个 'size' 组
// $itemsInSizeGroup 是一个 Collection,包含所有相同 'type' 和 'size' 的原始条目
// 计算当前 'type' 和 'size' 组合下的 'amount' 总和
// 注意:原始数据中的 'amount' 是字符串,这里需要进行类型转换以确保正确求和
$totalAmount = (int) $itemsInSizeGroup->sum('amount');
// 构建符合期望格式的新数据结构
// 期望格式中,每个 'size' 键对应一个包含单个对象的数组
return [
[
'type' => $type, // 从外层 map 的键获取 'type'
'size' => $size, // 从内层 map 的键获取 'size'
'amount' => $totalAmount, // 使用计算出的总和
]
];
});
});
// 将 Collection 转换为数组或 JSON 以便输出
$output = $processedData->toArray();
print_r(json_encode($output, JSON_PRETTY_PRINT));代码解析:
$lineItems-youjiankuohaophpcngroupBy(['type', 'size']):
->map(function (Collection $sizeGroups, string $type) { ... }):
return $sizeGroups->map(function (Collection $itemsInSizeGroup, string $size) { ... }):
$totalAmount = (int) $itemsInSizeGroup->sum('amount');:
return [[ ... ]]:
最终$output将精确匹配期望的输出格式。
以上就是Laravel Collection 高级聚合:分组、合并与字段求和的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号