PHP#数据Excel导出的一些策略
导出其实在任何类型的后端系统都比较常见,正规情况下excel导出的数据应该更多的用来做备份、底稿,理想情况下针对业务数据的任何业务操作都不应该依靠从后端业务系统导出数据然后人工进行干涉处理,但现实很骨感。。。
数据导出这类的功能在有条件的情况下是须要封装起来的,只有一个数据出口,在后续的业务规则调整变更时会真正的体现出它的价值。当一个典型的应用系统出现了五花八门的数据导出需求,说明这个应用系统的业务分析做的不够好或者很差,当用户只想用应用系统导出数据来辅助他们的业务流程时,最恐怖的情况就会是用户和数据库的距离只是隔着一个导出按钮。。。
环境
1.PHP5.5.14 (cli) (built: Sep 9 201419:09:25)
2.PHP Excel 1.7.8 (http://www.codeplex.com/PHPExcel)
处理逻辑
其实这类问题的关键点就是如何定义导出规则,定义的这套规则是否能适应业务流程,最基本的办法就是将数据导出的过程抽象为3个基础阶段,而后每个阶段可以再进行逐步的细化:
1.导出规则的定义
2.业务数据与导出规则的适配
3.导出规则解析构造Excel
示例
该示例实现了基本的导出功能,没有进行任何封装,没有关注性能或其它扩展性问题。
<?php
namespace org\ybygjy\comp\excel;
/**
* Excel组件封装
* <p>1.统一处理工程中针对excel的数据导出</p>
* @author WangYanCheng
* @version 2015-1-22
*/
class ExcelComp {
/**
* 构造函数
*/
public function __construct() {
require_once 'org/ybygjy/library/excel/PHPExcel.php';
}
/**
* 测试入口
*/
public function doTest() {
//构造原始数据
$dataArray = $this->buildData();
//导出
$this->doExportData($dataArray);
}
/**
* 解析传递的原始数据并导出
* @param $dataArr
*/
public function doExportData($dataArr) {
$phpObjExcel = new \PHPExcel();
$worksSheet = $phpObjExcel->setActiveSheetIndex(0);
//构造表头数据_Begin
$tmpColTitles = [];
$firstDataEntry = $dataArr[0];
//分配列索引
$colIndex = 0;
foreach($firstDataEntry as $key => $val) {
if (preg_match('/^_/', $key)) {
continue;
}
if (is_array($val)) {
//取array下的列名称
$val = $val[0];
$rowNums = count($val);
foreach ($val as $innerKey => $innerValue) {
$tmpColTitles[] = array(
'parentKey' => $key,
'key' => $innerKey,
'colIndex' => $colIndex
);
$colIndex++;
}
} else {
$tmpColTitles[] = array(
'key'=>$key,
'colIndex'=>$colIndex
);
$colIndex++;
}
}
for($i = 0; $i < count($tmpColTitles); $i++) {
$tmpObj = $tmpColTitles[$i];
$key = $tmpObj['key'];
$colIndex = $tmpObj['colIndex'];
$worksSheet->setCellValueByColumnAndRow($colIndex,1,$key);
}
//构造表头数据_End
//填充单元格数据
$currRow = 2;
foreach ($dataArr as $dataEntry) {
$mergeRow = $dataEntry['_DIMENSION'];
foreach ($tmpColTitles as $colEntry) {
$key = $colEntry['key'];
$colIndex = $colEntry['colIndex'];
$parentKey = (isset($colEntry['parentKey']) ? $colEntry['parentKey'] : null);
if (empty($parentKey)) {
$value = $dataEntry[$key];
if ($mergeRow == 1) {
$worksSheet->setCellValueByColumnAndRow($colIndex, $currRow, $value);
} else {
$worksSheet->mergeCellsByColumnAndRow($colIndex, $currRow, $colIndex, ($currRow + $mergeRow - 1))->setCellValueByColumnAndRow($colIndex, $currRow, $value);
}
} else {
$tmpDataArr = $dataEntry[$parentKey];
$innerRow = $currRow;
for($index = 0; $index < count($tmpDataArr); $index++) {
$innerDataEntry = $tmpDataArr[$index];
$value = $innerDataEntry[$key];
$worksSheet->setCellValueByColumnAndRow($colIndex, $innerRow, $value);
$innerRow++;
}
}
}
$currRow += $mergeRow;
}
header('Content-Type: application/vnd.ms-excel');
header('Content-Type: application/force-download');
header('Content-Type: application/octet-stream');
header('Content-Type: application/download');
header('Content-Disposition: attachment;filename="HelloWord.xls"');
header('Cache-Control: max-age=0');
header('Cache-Control: max-age=1');
header('Cache-Control: no-cache, must-revalidate');
header('Pragma: public');
$objWriter = \PHPExcel_IOFactory::createWriter($phpObjExcel, 'Excel5');
$objWriter->save('php://output');
}
/**
* 构造测试数据
* @return multitype:multitype:string number multitype:multitype:string
*/
private function buildData() {
$rtnData = array(
array(
'name'=>'YanCheng_01',
'age'=>'20',
'addr'=>array(
array(
'country'=>'China',
'province'=>'ShanDong'
),
array(
'country'=>'China',
'province'=>'BeiJing'
)
),
'_DIMENSION'=>2
),
array(
'name'=>'YanCheng_02',
'age'=>'21',
'addr'=>array(
array(
'country'=>'China',
'province'=>'LanZhou'
),
array(
'country'=>'China',
'province'=>'NingXia'
)
),
'_DIMENSION'=>2
),
array(
'name'=>'YanCheng_03',
'age'=>'22',
'addr'=>array(
array(
'country'=>'China',
'province'=>'JiaYuGuan'
)
),
'_DIMENSION'=>1
)
);
return $rtnData;
}
}