前言:
最近开发一款crm,使用PhalApi框架提供数据接口,要用公共接口来实现各个模块的数据导入导出功能。如果是单独的导入导出(一个模块一个导入导出接口)推荐大家使用 phpoffice/phpspreadsheet 。
我这里使用的是 PHPExcel,PhalApi官网上关于PHPExcel扩展,是基于PhalApi1.x 版本,这里使用的是 2.x 版本,所以这里使用的跟官网的略有不同。原因一:使用一个接口处理多个模块的导入或者导出;原因二:在导出模型的时候要做下拉选择框(在phpoffice/phpspreadsheet 文档中没有找到相关导出带下拉的内容,如果有了解的小伙伴可以给我留言哟)。crm后台各个模块都有各自对应的模型字段,可以自行设置字段的类型[文本、单选、多选、下拉、日期。。。]以及字段值。
例:客户模块的“客户意向”和“客户级别”字段:
首先,需要客户在导入数据之前,下载导入模型表,有灵活字段的情况下,需要在导出模型表的时候,对灵活字段进行处理,将单元格式设置为下拉选择框,从字段设置的值中选择,使用户只能从选项中选择,从根本上保证客户上传数据的有效性。
一:准备工作
(1)下载PHPExcel文件并解压,放在phalapi/vendor 文件夹下 。
下载地址:https://download.csdn.net/download/qq_32845825/11949643
(2)在文件头部引入PHPExcel
require dirname(dirname(dirname(__DIR__))).'/vendor/phpexcel/PHPExcel.php';
require dirname(dirname(dirname(__DIR__))).'/vendor/phpexcel/PHPExcel/Writer/Excel5.php';
require dirname(dirname(dirname(__DIR__))).'/vendor/phpexcel/PHPExcel/Writer/Excel2007.php';
require dirname(dirname(dirname(__DIR__))).'/vendor/phpexcel/PHPExcel/IOFactory.php';
require dirname(dirname(dirname(__DIR__))).'/vendor/phpexcel/PHPExcel/Cell.php';
use PHPExcel as NOP;
use PHPExcel_IOFactory;
use PHPExcel_Writer_Excel2007;
use PHPExcel_Writer_Excel5;
use PHPExcel_Cell;
二:下载数据导入模板
// 17 导出Excel模板
public function PostExportModelExcel($tablename) {
// 读取数据表的字段
$table_field = $this->config->get('common.TABLE_FIELD');
$field_list = $table_field[$tablename];
// 读取模型的模型ID
$model_id = $field_list['model_id'];
// 查询灵活字段以及值
$model_field_arr = $this->di->model_field->select("field,name,type,is_require,setting")->where(array("modelid"=>$model_id,"master_field"=>1))->order("id DESC")->limit(15)->fetchRows();
// 将模型字段增加到字段列表中
if (!empty($model_field_arr)) {
foreach ($model_field_arr as $key => $value) {
$field_list['filed'][$value['field']]['name'] = $value['name'];
$field_list['filed'][$value['field']]['type'] = $value['type'];
$field_list['filed'][$value['field']]['is_require'] = $value['is_require'];
$field_list['filed'][$value['field']]['setting'] = $value['setting'];
}
}
$customer_field_list = $field_list['filed'];
//引入phpexecl服务启动文件
$objPHPExcel =new NOP();
$objWriter = new \PHPExcel_Writer_Excel5($objPHPExcel);
$objWriter = new \PHPExcel_Writer_Excel2007($objPHPExcel);
$excelModel = new Excel();
//设置属性
$objProps = $objPHPExcel->getProperties();
$objProps->setCreator("WJZ");
$objProps->setLastModifiedBy("WJZ");
$objProps->setTitle("WJZ");
$objProps->setSubject("WJZ data");
$objProps->setDescription("WJZ data");
$objProps->setKeywords("WJZ data");
$objProps->setCategory("WJZ");
$objPHPExcel->setActiveSheetIndex(0);
$objActSheet = $objPHPExcel->getActiveSheet();
$objActSheet->setTitle($field_list['model_name'].'导入模板'.date('Y-m-d',time()));
//填充边框
$styleArray = [
'borders'=>[
'outline'=>[
'style'=>\PHPExcel_Style_Border::BORDER_THICK, //设置边框
'color' => ['argb' => '#F0F8FF'], //设置颜色
],
],
];
$k = 0;
$k = 0;
foreach ($customer_field_list as $field) {
$objActSheet->getColumnDimension($excelModel->stringFromColumnIndex($k))->setWidth(20); //设置单元格宽度
// 如果数据格式为下拉、多选、单选,对字段值进行处理
if ($field['type'] == 'select' || $field['type'] == 'checkbox' || $field['type'] == 'radio') {
if (!empty($field['setting']) && !is_array($field['setting'])) {
$setting = json_decode($field['setting'],true) ? : [];
} else {
$setting = $field['setting'];
}
$select_value = implode(',',$setting);
}
// 如果数据格式为sql,即该字段需要从其他表数据中获取值
if ($field['type'] == 'sql') {
$table_name = $field['from_table'];
$sql = "SELECT id,CONCAT_WS('_',id,title) as title FROM ".$this->prefix.$table_name." WHERE status = 1 LIMIT 50 ";
$setting_arr = $this->di->$table_name->queryAll($sql);
$setting = array_column($setting_arr, "title");
$select_value = implode(',',$setting);
}
// 如果字段有值,将该字段所在列设置为下拉选择框
if ($select_value) {
for ($c=3; $c<=70; $c++) {
//数据有效性 start
$objValidation = $objActSheet->getCell($excelModel->stringFromColumnIndex($k).$c)->getDataValidation(); //这一句为要设置数据有效性的单元格
$objValidation -> setType(\PHPExcel_Cell_DataValidation::TYPE_LIST)
-> setErrorStyle(\PHPExcel_Cell_DataValidation::STYLE_INFORMATION)
-> setAllowBlank(false)
-> setShowInputMessage(true)
-> setShowErrorMessage(true)
-> setShowDropDown(true)
-> setErrorTitle('输入的值有误')
-> setError('您输入的值不在下拉框列表内.')
-> setPromptTitle('--请选择--')
-> setFormula1('"'.$select_value.'"');
//数据有效性 end
}
}
//检查该字段若必填,加上"*"
$field['name'] = \App\sign_required($field['is_require'], $field['name']);
$objActSheet->getStyle($excelModel->stringFromColumnIndex($k).'2')->applyFromArray($styleArray);//填充样式
$objActSheet->setCellValue($excelModel->stringFromColumnIndex($k).'2', $field['name']);
$k++;
}
$max_customer_column = $excelModel->stringFromColumnIndex($k-1);
$mark_customer = $excelModel->stringFromColumnIndex($k);
$objActSheet->mergeCells('A1:'.$max_customer_column.'1');
$objActSheet->getStyle('A1:'.$mark_customer.'1')->getAlignment()->setHorizontal(\PHPExcel_Style_Alignment::HORIZONTAL_CENTER); //水平居中
$objActSheet->getStyle('A1:'.$mark_customer.'1')->getAlignment()->setVertical(\PHPExcel_Style_Alignment::VERTICAL_CENTER); //垂直居中
$objActSheet->getRowDimension(1)->setRowHeight(28); //设置行高
$objActSheet->getStyle('A1')->getFont()->getColor()->setARGB('FFFF0000');
$objActSheet->getStyle('A1')->getAlignment()->setWrapText(true);
//给单元格填充背景色
$objActSheet->getStyle('A1:'.$max_customer_column.'1')->getFill()->setFillType(\PHPExcel_Style_Fill::FILL_SOLID)->getStartColor()->setARGB('#ff9900');
$content = $field_list['model_name'].'信息(*代表必填项)';
$objActSheet->setCellValue('A1', $content);
$objActSheet->getStyle('A1:'.$max_customer_column.'1')->getBorders()->getAllBorders()->setBorderStyle(\PHPExcel_Style_Border::BORDER_THIN);
$objActSheet->getStyle('A1')->getBorders()->getRight()->getColor()->setARGB('#000000');
$objWriter = PHPExcel_IOFactory::createWriter($objPHPExcel, 'Excel2007');
ob_end_clean();
header("Content-Type: application/vnd.ms-excel;");
header("Content-Disposition:attachment;filename=".$field_list['model_name']."信息导入模板".date('Y-m-d',time()).".xls");
header("Pragma:no-cache");
header("Expires:0");
$objWriter->save('php://output');
return $rs = array('code'=>1,'msg'=>'000000','data'=>array(),'info'=>array());
}
三:导入数据
// 18 Excel文件导入
public function PostImportExcel($type,$file,$uid) {
// 查看文件格式是否正确
if ($file['error'] == 4) {
return array('code'=>0,'msg'=>'000080','data'=>array(),'info'=>array());
}
$ext = \APP\getExtension($file['name']);//获取文件后缀
$excel_type = array('xls','csv','xlsx');
if (!in_array($ext,$excel_type)){
return array('code'=>0,'msg'=>'000081','data'=>array(),'info'=>array());
}
// 上传文件
$upload_arr = $this->UploadFile($file,4);
if ($upload_arr['data']['file_path'] == "") {
return array('code'=>0,'msg'=>'000082','data'=>array(),'info'=>array());
}
$savePath = dirname(dirname(dirname(__DIR__))).$upload_arr['data']['file_path'];
$admin_common = new AdminCommon();
$customer_common = new CustomerCommon();
// 数据处理
$table_field = $this->config->get('common.TABLE_FIELD');
$field_arr = $table_field[$type];
$field_list = $field_arr['filed'];//字段列表
$model_id = $field_arr['model_id'];//模型ID
// 查询灵活字段以及值
$model_field_arr = $this->di->model_field->select("field,name,type,is_require,setting")->where(array("modelid"=>$model_id,"master_field"=>1))->order("id DESC")->limit(15)->fetchRows();
$model_field_name_arr = array();
if (!empty($model_field_arr)) {
foreach ($model_field_arr as $key => $value) {
$model_field_name_arr[] = $value['name'];
$field_list[$value['field']]['name'] = $value['name'];
$field_list[$value['field']]['type'] = $value['type'];
$field_list[$value['field']]['is_require'] = $value['is_require'];
$field_list[$value['field']]['setting'] = $value['setting'];
}
}
$fieldArr = [];
$requireField = []; //必填字段
foreach ($field_list as $key => $value) {
$fieldArr[$value['name']]['name'] = $key;
$fieldArr[$value['name']]['type'] = $value['type'];
if ($value['is_require'] == 1) $requireField[] = $key;
if (isset($value['setting'])) {
$fieldArr[$value['name']]['setting'] = $value['setting'];
}
}
$field_num = count($fieldArr);//字段个数
//默认数据
$defaultData = array();
$user_info = $this->di->user->select("username,structure_id")->where(array("id"=>$uid))->fetchOne();
$defaultData['publisher'] = $user_info['username'];
$defaultData['addtime'] = time();
$defaultData['updatetime'] = time();
$defaultData['status'] = 1;
$defaultData['sort'] = 0;
// 验重字段
switch ($type) {
case 'customer':
$uniqueField = 'cname';
break;
default:
$uniqueField = 'title';
break;
}
$objPHPExcel =new NOP();
if ($ext =='xlsx') {
$objWriter = new \PHPExcel_Writer_Excel2007($objPHPExcel);
$objRender = \PHPExcel_IOFactory::createReader('Excel2007');
// $objRender->setReadDataOnly(true);
$ExcelObj = $objRender->load($savePath);
} elseif ($ext =='xls') {
$objWriter = new \PHPExcel_Writer_Excel2005($objPHPExcel);
$objRender = \PHPExcel_IOFactory::createReader('Excel2005');
// $objRender->setReadDataOnly(true);
$ExcelObj = $objRender->load($savePath);
} elseif ($ext=='csv') {
$objWriter = new \PHPExcel_Reader_CSV($objPHPExcel);
//默认输入字符集
$objWriter->setInputEncoding('UTF-8');
//默认的分隔符
$objWriter->setDelimiter(',');
//载入文件
$ExcelObj = $objWriter->load($savePath);
}
$currentSheet = $ExcelObj->getSheet(0);
//查看有几个sheet
$sheetContent = $ExcelObj->getSheet(0)->toArray();
//读取表头
$excelHeader = $sheetContent[1];
unset($sheetContent[0]);
unset($sheetContent[1]);
$keys = 2;
$errorMessage = [];
foreach ($sheetContent as $kk => $val) {
$data = '';
$k = 0;
$resNameIds = '';
$keys++;
$name = ''; //客户、线索、联系人等名称
$data = $defaultData; //导入数据
$resWhere = ''; //验重条件
$resInfo = false; //Excel列是否有数据
$resultInfo = true;
foreach ($excelHeader as $aa => $header) {
if (empty($header)) break;
$fieldName = trim(str_replace('*','',$header));//去掉表头的*
$info = '';
$info = $val[$k];
if ($info) $resInfo = true;
// 判断必填项是否填写
if ( in_array($fieldArr[$fieldName]['name'],$requireField) && !$info ) {
$errorMessage[] = '第'.$keys.'行导入错误,失败原因:'.$fieldName.'必填';
$resultInfo = false;
break;
}
// 验重
if (strstr($fieldName,"名称") !== false ) {
$info_id = $this->di->$type->where(array($uniqueField=>$val[$k]))->fetchOne("id");
if ($info_id) {
$errorMessage[] = '第'.$keys.'行导入错误,失败原因:'.$field_arr['model_name'].'名称已存在!';
$resultInfo = false;
break;
}
}
// 整理数据
if (empty($fieldArr[$fieldName]['name'])) continue;
if ($aa <= $field_num) {
$resList = [];
$resList = $this->sheetData($k, $fieldArr, $fieldName, $info, $model_field_name_arr, $type);
$resData[] = $resList['data'];
$k = $resList['k'];
}
}
if ($resultInfo) {
$result = $this->changeArr($resData); //二维数组转一维数组
$data = $result ? array_merge($data,$result) : [];
$resDataId = $this->di->$type->insert($data);
$resDataId = $this->di->$type->insert_id();
if (!$resDataId) {
$errorMessage[] = '第'.$keys.'行导入错误,失败原因:数据错误';
break;
}
}
}
return $rs = array('code'=>1,'msg'=>'000000','data'=>$errorMessage,'info'=>$errorMessage);
}
/**
* excel数据处理
* @author W J Z
* @DateTime 2019-11-01T16:31:44+0800
* @param integer $k [行号]
* @param [array] $fieldArr [字段列表]
* @param [type] $fieldName [当前字段名称]
* @param [type] $info [当前数据]
* @param [type] $model_field_name_arr [灵活字段列表]
* @param [type] $type [类型(区分表)]
* @return [array] $res [处理好的数据类型]
*/
public function sheetData($k = 0, $fieldArr, $fieldName, $info, $model_field_name_arr,$type) {
if ($info) {
if ($fieldArr[$fieldName]['type'] == 'address') {
$address = array();
for ($i=0; $i<3; $i++) {
$address[] = $val[$k];
$k++;
}
$data[$fieldArr[$fieldName]['name']] = implode(chr(10), $address);
} elseif ($fieldArr[$fieldName]['type'] == 'sql') {
// 如果字段类型为sql 存储关联ID
$info_arr = explode('_',$info);
$data[$fieldArr[$fieldName]['name']] = $info_arr[0];
$k++;
} elseif ($fieldArr[$fieldName]['type'] == 'date') {
// 如果字段类型为date
$data[$fieldArr[$fieldName]['name']] = $info ? date('Y-m-d',strtotime($info)) : '';
$k++;
} elseif ($fieldArr[$fieldName]['type'] == 'datetime') {
// 如果字段类型为datetime
$data[$fieldArr[$fieldName]['name']] = $info ? strtotime($info) : 0;
$k++;
} elseif (!in_array($fieldName,$model_field_name_arr) && $fieldArr[$fieldName]['type'] == 'radio' ) {
// 非自定义字段&&选择框类型
$setting = $fieldArr[$fieldName]['setting'];
foreach ($setting as $sk => $sv) {
if ($info == $sv) {
$setting_value = $sk;
break;
}
}
$data[$fieldArr[$fieldName]['name']] = $setting_value ? $setting_value : 0;
$k++;
} elseif (in_array($fieldName,$model_field_name_arr) && ($fieldArr[$fieldName]['type'] == 'radio' || $fieldArr[$fieldName]['type'] == 'select') ) {
// 自定义字段&&[单选||下拉]
$data[$fieldArr[$fieldName]['name']] = strval($info);
$k++;
} elseif (in_array($fieldName,$model_field_name_arr) && $fieldArr[$fieldName]['type'] == 'checkbox') {
// 自定义字段&&多选
$info_arr = explode(',',$info);
$data[$fieldArr[$fieldName]['name']] = json_encode($info_arr,JSON_UNESCAPED_UNICODE);
$k++;
}else {
// 文本类[这里注意,要使用strval函数对数据进行处理,否则,遇到手机号、金钱这种类型的,存储到数据库会末尾多出很多0]
$data[$fieldArr[$fieldName]['name']] = strval($info) ? : '';
$k++;
}
} else {
// 如果数据为空,对特定的一些数据进行处理
if ($fieldArr[$fieldName]['type'] == 'price' || $fieldArr[$fieldName]['type'] == 'datetime') {
$data[$fieldArr[$fieldName]['name']] = 0;
} else {
$data[$fieldArr[$fieldName]['name']] = '';
}
$k++;
}
$res['data'] = $data;
$res['k'] = $k;
return $res;
}
//二维数组转一维数组
public function changeArr($arr)
{
$newArr = [];
foreach ($arr as $v) {
if ($v && is_array($v)) {
$newArr = array_merge($newArr,$v);
} else {
continue;
}
}
return $newArr;
}
四:总结
经过上面的代码处理,就可以完成下载导入模型以及数据的导入了。如果需要导出数据,只需要在第二步的处理中,查询需要导出的数据,并且稍作修改就可以了。今天就到这里了,下次会掌握什么技能呢?下次见咯~