Laravel多数据库统计导出CSV异常排查及优化建议
问题描述
我编写了一个Laravel命令脚本,用于遍历多个数据库统计对象数量,匹配「名称-对象数量」并导出为CSV文件。核心代码如下:
$formatted_data = array(); $providers = provider::where('del', 'no')->get(); foreach($providers as $provider){ $formatted_data[$provider['id']]['name'] = $provider['name']; } $objectMappingsModels = array((new objectMapping1), (new objectMapping2), (new objectMapping3), (new objectMapping4), (new objectMapping5), (new objectMapping6), (new objectMapping7), (new objectMapping8)); foreach($objectMappingsModels as $objectMappingsModel){ $totals = $objectMappingsModel::select('providerFK', DB::raw('count(*) as total'), DB::raw('monthName(ut) as month_name') ) ->where('userFK', '!=', 1) ->where('del', 'no') ->whereMonth('ut', $this->option('month')) ->whereYear('ut', $this->option('year')) ->groupBy('providerFK', 'month_name') ->get() ->toArray(); foreach($totals as $total){ $formatted_data[$total['providerFK']]['count'] = $total['total']; } } $responce = Excel::store(new UsersExport($formatted_data), 'testNameDate.csv',null, \Maatwebsite\Excel\Excel::CSV); return true;
导出类代码:
class UsersExport implements FromArray { protected $invoices; public function __construct(array $invoices) { $this->invoices = $invoices; } public function array(): array { return $this->invoices; } }
目前脚本运行存在异常:统计结果偶尔不准确,且导出的CSV最后一行总是只有随机数字(无关联名称)。
问题原因分析
1. 统计结果不准确
- 多模型遍历统计时,后一个模型的统计结果会直接覆盖前一个模型同
providerFK的count值,最终仅保留最后一个模型的统计数据,导致总统计量缺失。 - 未初始化
count字段,无数据的服务商对应的count会缺失,导出CSV时易出现数据错位或统计遗漏。
2. CSV最后一行出现随机数字
$formatted_data是关联数组,键为服务商ID,Maatwebsite Excel导出时会将数组键额外输出到CSV中,所谓的"随机数字"实际是某个服务商的ID,因关联数组的导出逻辑导致键被错误展示。- 数组中存在未正确初始化的空元素/键值对,引发导出时的异常行。
代码优化方案
1. 修复统计逻辑,累加多模型数据
将count的覆盖逻辑改为累加,同时初始化每个服务商的count为0,确保无数据时显示0:
$formatted_data = []; $providers = provider::where('del', 'no')->get(); foreach($providers as $provider){ // 初始化name和count字段,count默认0 $formatted_data[$provider['id']] = [ 'name' => $provider['name'], 'count' => 0 ]; } $objectMappingsModels = [ new objectMapping1, new objectMapping2, new objectMapping3, new objectMapping4, new objectMapping5, new objectMapping6, new objectMapping7, new objectMapping8 ]; foreach($objectMappingsModels as $objectMappingsModel){ $totals = $objectMappingsModel::select('providerFK', DB::raw('count(*) as total')) ->where('userFK', '!=', 1) ->where('del', 'no') ->whereMonth('ut', $this->option('month')) ->whereYear('ut', $this->option('year')) ->groupBy('providerFK') ->get() ->toArray(); foreach($totals as $total){ // 累加每个模型的统计量,而非覆盖 if(isset($formatted_data[$total['providerFK']])){ $formatted_data[$total['providerFK']]['count'] += $total['total']; } } }
2. 修复CSV导出异常问题
修改导出类,将关联数组转为索引数组,同时添加表头,避免键被输出:
use Maatwebsite\Excel\Concerns\FromArray; use Maatwebsite\Excel\Concerns\WithHeadings; class UsersExport implements FromArray, WithHeadings { protected $data; public function __construct(array $data) { // 将关联数组转为索引数组,只保留值部分 $this->data = array_values($data); } public function array(): array { return $this->data; } // 添加CSV表头 public function headings(): array { return ['服务商名称', '对象数量']; } }
3. 其他优化点
- 使用短数组语法
[]替代array(),符合现代PHP规范。 - 移除不必要的
month_name查询,已通过whereMonth和whereYear限定时间,无需再按月份名称分组。 - 增加
isset判断,避免不存在的providerFK导致数组下标错误。 - 导出时添加时间戳到文件名,避免文件覆盖:
$fileName = 'provider_stats_' . date('YmdHis') . '.csv'; $response = Excel::store(new UsersExport($formatted_data), $fileName, null, \Maatwebsite\Excel\Excel::CSV);
内容的提问来源于stack exchange,提问作者Yorkata008
相关产品推荐
相关产品推荐

