Yii2本身不提供Excel导入导出能力,必须依赖第三方库;直接用phpoffice/phpspreadsheet是当前最稳妥的选择,但需手动处理模型映射、数据校验和内存控制。

Yii2 本身不提供 Excel 导入导出能力,必须依赖第三方库;直接用 phpoffice/phpspreadsheet 是当前最稳妥的选择,但需手动处理模型映射、数据校验和内存控制。
为什么不用 yii2-export 或 yii2-phpexcel?
这两个扩展已多年未维护:yii2-phpexcel 基于已废弃的 PHPExcel,读大文件极易内存溢出;yii2-export 虽封装了 phpspreadsheet,但对导入场景支持弱,无法灵活拦截脏数据或中断解析。实际项目中,你几乎一定会遇到字段映射错位、空行跳过、日期格式识别失败等问题,而这些都需要直接调用 PhpSpreadsheet 的 API 控制。
导入时如何避免内存爆掉和类型错乱?
Excel 文件哪怕只有几万行,全量加载进内存也会触发 PHP 内存限制。关键不是“怎么读”,而是“怎么边读边校验边入库”。
- 用
Reader::load()后立即调用$reader->setReadDataOnly(true),跳过公式、样式等无关内容 - 对 .xlsx 文件优先用
XmlReader(非默认),它支持流式读取:用$reader->listWorksheetInfo($filename)先确认工作表结构,再用$reader->getSheetNames()定位目标 sheet - 逐行遍历必须用
foreach ($worksheet->getRowIterator() as $row),而非$worksheet->toArray()—— 后者会一次性把整表转成数组 - 日期列要手动转换:
PhpOffice\PhpSpreadsheet\Shared\Date::excelToDateTimeObject($cellValue),否则读出来是数字(如 44197) - 空行判断别只看第一列:
array_filter($rowData, 'strlen')比!empty($rowData[0])更可靠
导出时怎样动态生成多 sheet 且控制列宽?
导出比导入更易踩坑的地方在于样式和性能:合并单元格写错会导致 Excel 打不开;设置整列宽度不生效是因为没指定范围。
- 添加新 sheet 必须用
$spreadsheet->createSheet(),然后$spreadsheet->setActiveSheetIndex(1)切换,最后$spreadsheet->getActiveSheet()->setTitle('明细') - 列宽不能只写
setWidth(15),得先获取列维度:$spreadsheet->getActiveSheet()->getColumnDimension('A')->setWidth(20) - 冻结首行:用
$spreadsheet->getActiveSheet()->freezePane('A2'),注意是 A2 不是 A1 - 数值型字段导出为文本(如工号带前导零),得设单元格格式:
$objPHPExcel->getActiveSheet()->getStyle('B1:B1000')->getNumberFormat()->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_TEXT) - 超 10 万行导出时,关闭计算引擎:
$spreadsheet->setCalculationEngine(new \PhpOffice\PhpSpreadsheet\Calculation\Calculation(null))可提速 30%+
事务与错误反馈怎么和 Yii2 的 Model 绑定?
Excel 导入本质是批量操作,但用户需要知道第几行、哪个字段错了。不能简单 throw Exception,得收集所有错误并返回结构化结果。
- 在循环内对每行创建临时
ActiveRecord实例,调用$model->load($data, '')再$model->validate(),错误存入$errors[] = ['row' => $i, 'field' => 'phone', 'message' => '手机号格式错误'] - 数据库写入必须包在
Transaction中:$transaction = Yii::$app->db->beginTransaction(),任一行失败就$transaction->rollBack() - 成功导入后,用
Yii::$app->session->setFlash('success', "成功导入 {$successCount} 条"),失败则渲染错误列表页,把$errors传过去表格展示 - 注意:
phpspreadsheet的异常(如Spreadsheet_Exception)和 Yii2 的Model验证错误要分开捕获,前者代表文件损坏,后者才是业务逻辑问题
真正难的不是读写 Excel,而是把 Excel 的松散结构映射到 ActiveRecord 的强约束上——比如“入职日期”列可能填了“2023-01”、“23/1/1”、“一月”甚至空白,这些都要在导入前定义清洗规则。没人能绕过这层适配,封装得越厚,出问题时越难定位。


















