基于PhalApi的PHPExcel扩展实现数据导入导出

作者: siediyer 分类: PHP 发布时间: 2022-10-28 11:21

前言:
最近开发一款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;
}

四:总结

经过上面的代码处理,就可以完成下载导入模型以及数据的导入了。如果需要导出数据,只需要在第二步的处理中,查询需要导出的数据,并且稍作修改就可以了。今天就到这里了,下次会掌握什么技能呢?下次见咯~

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!

Title - Artist
0:00