Yii2框架GridView自带导出功能最佳实践

时间:2022-08-16 15:00:00

1. 导出excel的实现方法

(1)使用phpexcel封装工具类导出excel

(2)使用爬虫爬取页面再处理封装工具类导出excel

(3)使用页面渲染后处理html添加头部信息生成excel文件的js导出

(4)使用GridView视图组件自带的导出功能

2.代码实现(使用GridView视图组件自带的导出功能)

<?= kartik\grid\GridView::widget([
    'tableOptions' => ['class' => 'table table-striped', 'style'=>'font-size:12px;'],
    'layout' => "<div class=\"pull-left div_title\" >库存盘点清单</div><div class=\"pull-right\">{toolbar}</div><div class=\"clearfix\"></div>{items}",
    'export'=>[
        'label'=>'导出',
        'target'=>kartik\grid\GridView::TARGET_BLANK,
    ],
    'exportConfig'=>[
        \kartik\grid\GridView::EXCEL => [
            'label' => Yii::t('app', '导出Excel'),
            'icon' =>'file-excel-o',
            'iconOptions' => ['class' => 'text-success'],
            'showHeader' => true,
            'showPageSummary' => true,
            'showFooter' => true,
            'showCaption' => true,
            'filename' => Yii::t('app', '库存盘点清单'),
            'alertMsg' => Yii::t('app', '将生成并下载Excel文件'),
            'options' => ['title' => Yii::t('app', 'Microsoft Excel 95+')],
            'mime' => 'application/vnd.ms-excel',
            'config' => [
                'worksheet' => Yii::t('app', '库存盘点清单'),
                'cssFile' => ''
            ]
        ],
    ],
    'striped'=>false,
    'hover'=>true,
    'showHeader'=>true,
    'showFooter'=>false,
    'showPageSummary' => false,
    'showOnEmpty'=>true,
    'emptyText'=>'当前没有数据!',
    'emptyTextOptions'=>['style'=>'color:red;font-weight:bold;text-align:center;'],
    'dataProvider' => $dataProvider,
    'columns' => $columns,
]); ?>

优点:

(1)代码实现简单,只需在GridView中配置即可

(2)导出的Excel文件格式与GridView相同,包括对齐方式,自动实现统计导出

缺点:

(1)导出按钮的放置位置不方便调整,只能放置在紧贴GridView的位置

3.代码实战(复用searchModel进行数据查询,并使用phpexcel封装工具类导出excel)

(1)封装的Excel导出工具类代码如下:

<?php

namespace core\components;

use PHPExcel;
use PHPExcel_IOFactory;
use PHPExcel_Style_Alignment;
use PHPExcel_Reader_Excel5;
use PHPExcel_RichText;

class MyExcelHelper extends \yii\base\Component{

    /**
     * 将二维数组的数据转化为excel表格导出
     * @param $data
     * @param $excel_name
     * @param $headers
     * @param $options
     */
    public static function array2excel($data, $excel_name, $headers, $options, $style_options){
        $objPHPExcel = new PHPExcel();
        ob_start();

        if (!isset($options['creator'])){
            $objPHPExcel->getProperties()->setCreator('creator');
        }else{
            $objPHPExcel->getProperties()->setCreator($options['creator']);
        }

        if (isset($options['last_modified_by'])){
            $objPHPExcel->getProperties()->setCreator('last_modified_by');
        }else{
            $objPHPExcel->getProperties()->setCreator($options['last_modified_by']);
        }

        if (isset($options['title'])){
            $objPHPExcel->getProperties()->setCreator('title');
        }else{
            $objPHPExcel->getProperties()->setCreator($options['title']);
        }

        if (isset($options['subject'])){
            $objPHPExcel->getProperties()->setCreator('subject');
        }else{
            $objPHPExcel->getProperties()->setCreator($options['subject']);
        }

        if (isset($options['description'])){
            $objPHPExcel->getProperties()->setCreator('description');
        }else{
            $objPHPExcel->getProperties()->setCreator($options['description']);
        }

        if (isset($options['keywords'])){
            $objPHPExcel->getProperties()->setCreator('keywords');
        }else{
            $objPHPExcel->getProperties()->setCreator($options['keywords']);
        }

        if (isset($options['category'])){
            $objPHPExcel->getProperties()->setCreator('category');
        }else{
            $objPHPExcel->getProperties()->setCreator($options['category']);
        }

        $header_keys = array_keys($headers);

        foreach ($header_keys as $header_index => $header_key){
            $index_ascii = $header_index + 65;
            $index_chr = chr($index_ascii);
            $header_value = $headers[$header_key];
            $objPHPExcel->setActiveSheetIndex(0)->setCellValue($index_chr.'1', $header_value);
            $objPHPExcel->setActiveSheetIndex(0)->getColumnDimension($index_chr)->setWidth(20);
            $objPHPExcel->setActiveSheetIndex(0)->getStyle($index_chr.'1')->applyFromArray([
                'font'=>[
                    'bold' => true
                ],
                'alignment'=>[
                    'horizontal' => PHPExcel_Style_Alignment::HORIZONTAL_CENTER
                ]
            ]);
            if (isset($style_options['h_align'][$header_key])){
                if ($style_options['h_align'][$header_key] == 'left'){
                    $h_align = PHPExcel_Style_Alignment::HORIZONTAL_LEFT;
                }elseif ($style_options['h_align'][$header_key] == 'center'){
                    $h_align = PHPExcel_Style_Alignment::HORIZONTAL_CENTER;
                }elseif ($style_options['h_align'][$header_key] == 'right'){
                    $h_align = PHPExcel_Style_Alignment::HORIZONTAL_RIGHT;
                }else{
                    $h_align = PHPExcel_Style_Alignment::HORIZONTAL_CENTER;
                }
                $objPHPExcel->setActiveSheetIndex(0)->getStyle($index_chr)->applyFromArray([
                    'alignment'=>[
                        'horizontal' => $h_align
                    ]
                ]);
            }
        }

        $data_row_index = 2;
        foreach ($data as $row_index => $row){
            $data_keys = array_keys($row);
            foreach ($data_keys as $column_index => $data_key){
                if ($column_index>=26){
                    throw new \yii\base\Exception('EXCEL表格超过26列');
                }
                $index_ascii = $column_index + 65;
                $index_chr = chr($index_ascii);
                $value = $row[$data_key];
                $objPHPExcel->setActiveSheetIndex(0)->setCellValue($index_chr . $data_row_index, $value);
            }
            $data_row_index++;
        }

        if (isset($options['summary'])){
            $summary_keys = array_keys($options['summary']);
            foreach ($summary_keys as $summary_index => $summary_key){
                $index_ascii = $summary_index + 65;
                $index_chr = chr($index_ascii);
                $summary_flag = $options['summary'][$summary_key];
                if ($summary_flag){
                    $summary_value = \core\components\ArrayHelper::sumByColumn($data, $summary_key);
                    $objPHPExcel->setActiveSheetIndex(0)->setCellValue($index_chr . $data_row_index, $summary_value);
                }
            }
        }

        $objPHPExcel->setActiveSheetIndex(0);

        $objWriter = PHPExcel_IOFactory::createWriter($objPHPExcel, 'Excel5');

        ob_end_clean();

        header('Content-Type: application/vnd.ms-excel');
        header('Content-Disposition: attachment;filename="' . $excel_name . '.xls"');
        header('Cache-Control: max-age=0');

        $objWriter->save('php://output');
        exit;
    }

    /**
     * 将excel表格转化为二维数组的数据
     * @param $excel_path
     * @param $data
     * @param $header
     */
    public static function excel2array($excel_path, $header_keys){

        if(!file_exists($excel_path)){
            throw new \yii\base\Exception('该EXCEL不存在!');
        }
        $PHPReader = new \PHPExcel_Reader_Excel2007();
        if(!$PHPReader->canRead($excel_path)){
            $PHPReader = new PHPExcel_Reader_Excel5();
            if(!$PHPReader->canRead($excel_path)){
                throw new \yii\base\Exception('该EXCEL不可读');
            }
        }

        $PHPExcel = $PHPReader->load($excel_path);
        $currentSheet = $PHPExcel->getSheet(0);

        $max_column_index = $currentSheet->getHighestColumn();
        $max_row_index = $currentSheet->getHighestRow();

        $data = array();
        for($row_index=2; $row_index<=$max_row_index; $row_index++ ){
            for($column_chr='A'; $column_chr<=$max_column_index; $column_chr++){
                $column_ord = ord($column_chr);
                $column_index = $column_ord - 65;
                $key = $column_chr.$row_index;
                $value = $currentSheet->getCell($key)->getValue();
                if($value instanceof PHPExcel_RichText){
                    $value = $value->__toString();
                }

                $data[$row_index-1][$header_keys[$column_index]] = $value;
            }
        }
        return $data;
    }
}

(2)导出业务逻辑类:

<?php

namespace backend\models;

use Yii;

class SheetExportAdapter{

    //在适配器中引用数据查询模型
    public $searchModel;

    //构造方法,传入数据查询模型,引用进行数据的查询
    public function __construct($searchModel)
    {
        $this->searchModel = $searchModel;
    }

    /**
     * 导出业务逻辑接口
     */
    public function export(){}
}

class WmsPartiallyProductInSheetExport extends SheetExportAdapter
{
    /**
     * 导出类的业务逻辑实现,先从搜索模型中获取参数,再调用搜索模型进行数据查询,最后调用导出工具类进行导出
     */
    public function export(){
        $params = [];
        $params['WmsPartiallyProductInSheetSearch']['search_type'] = $this->searchModel->search_type;
        $params['WmsPartiallyProductInSheetSearch']['common_producer_info_id'] = $this->searchModel->common_producer_info_id;
        $params['WmsPartiallyProductInSheetSearch']['common_producer_herb_info_id_product'] = $this->searchModel->common_producer_herb_info_id_product;
        $params['WmsPartiallyProductInSheetSearch']['begin_at'] = $this->searchModel->begin_at;
        $params['WmsPartiallyProductInSheetSearch']['end_at'] = $this->searchModel->end_at;
        $dataProvider = $this->searchModel->search($params, true);
        $data = [];
        foreach ($dataProvider->getModels() as $model){
            $wms_partially_product_in_sheet_number = $model->wms_partially_product_in_sheet_number;
            if ($model->is_del == 1) {
                $wms_partially_product_in_sheet_number .= '('.$model->stock_origin_type.')(已作废)';
            }else{
                $wms_partially_product_in_sheet_number .= '('.$model->stock_origin_type.')';
            }
            $common_producer_herb_info_name_product = $model->common_producer_herb_info_name_product;
            $common_producer_herb_grade_name_product = $model->common_producer_herb_grade_name_product;
            $common_producer_herb_place_info_name = $model->common_producer_herb_place_info_name;
            if (\core\models\WmsManager::PIECE_TYPE_STANDARD == $model->piece_type){
                $piece_type_name = '标准件';
            }elseif(\core\models\WmsManager::PIECE_TYPE_OFF_STANDARD == $model->piece_type){
                $piece_type_name = '非标准件';
            }else{
                $piece_type_name = '未设置';
            }
            if (!\core\models\WmsManager::getIsShowWeightPerPackage($model->piece_type)){
                $wms_partially_product_in_sheet_weight_per_package = '无';
            }else{
                $wms_partially_product_in_sheet_weight_per_package = \common\models\Base::weightBcdiv($model->wms_partially_product_in_sheet_weight_per_package);
            }
            if (empty($model->wms_partially_product_in_sheet_package_number)){
                $wms_partially_product_in_sheet_package_number = '未设置';
            }else{
                $wms_partially_product_in_sheet_package_number = \core\models\WmsManager::showTotalPackageNumber($model->wms_partially_product_in_sheet_package_number, $model->standard_package_number, $model->off_standard_package_number, $model->piece_type);
            }
            $wms_partially_product_in_sheet_in_weight = \common\models\Base::weightBcdiv($model->wms_partially_product_in_sheet_in_weight);
            $modelDetail = \core\models\WmsStockDetailInfo::findNotDel()->where([
                'wms_stock_detail_info_relation_good_in_sheet_number' => $model->wms_partially_product_in_sheet_number,
                'common_producer_material_type_info_id' =>  \core\models\WmsStockDetailInfo::wmsMaterialType()['partiallyProductType']])->one();
            $surplus_weight =  $modelDetail&&$modelDetail->wms_stock_detail_info_weight?\common\models\Base::weightBcdiv($modelDetail->wms_stock_detail_info_weight):0;
            if (empty($model->wms_partially_product_in_sheet_product_in_date)){
                $wms_partially_product_in_sheet_product_in_date = '未知';
            }else{
                $wms_partially_product_in_sheet_product_in_date = date('Y-m-d', $model->wms_partially_product_in_sheet_product_in_date);
            }
            $wms_partially_product_in_sheet_status_name = (!$model->wms_partially_product_in_sheet_status||$model->wms_partially_product_in_sheet_status==0)?'未确认入库':"已入库";
            $wmsPartiallyProductInSheetQualityCheckNumber = $model->getWmsPartiallyProductInSheetQualityCheckNumber();
            if ('<span style="color: red;">未质检</span>' == $wmsPartiallyProductInSheetQualityCheckNumber){
                $wmsPartiallyProductInSheetQualityCheckNumber = '未质检';
            }
            $data[] = [
                'wms_partially_product_in_sheet_number'=>$wms_partially_product_in_sheet_number,
                'common_producer_herb_info_name_product'=>$common_producer_herb_info_name_product,
                'common_producer_herb_grade_name_product'=>$common_producer_herb_grade_name_product,
                'common_producer_herb_place_info_name'=>$common_producer_herb_place_info_name,
                'piece_type_name'=>$piece_type_name,
                'wms_partially_product_in_sheet_weight_per_package'=>$wms_partially_product_in_sheet_weight_per_package,
                'wms_partially_product_in_sheet_package_number'=>$wms_partially_product_in_sheet_package_number,
                'wms_partially_product_in_sheet_in_weight'=>$wms_partially_product_in_sheet_in_weight,
                'surplus_weight'=>$surplus_weight,
                'wms_partially_product_in_sheet_product_in_date'=>$wms_partially_product_in_sheet_product_in_date,
                'wms_partially_product_in_sheet_status_name'=>$wms_partially_product_in_sheet_status_name,
                'wmsPartiallyProductInSheetQualityCheckNumber'=>$wmsPartiallyProductInSheetQualityCheckNumber,
            ];
        }

        $excel_name = '半成品入库单';
        $headers = [
            'wms_partially_product_in_sheet_number'=>'入库单号',
            'common_producer_herb_info_name_product'=>'半成品名称',
            'common_producer_herb_grade_name_product'=>'半成品等级',
            'common_producer_herb_place_info_name'=>'半成品产地',
            'piece_type_name'=>'计件类型',
            'wms_partially_product_in_sheet_weight_per_package'=>'包装规格',
            'wms_partially_product_in_sheet_package_number'=>'入库件数',
            'wms_partially_product_in_sheet_in_weight'=>'入库重量(公斤)',
            'surplus_weight'=>'剩余重量(公斤)',
            'wms_partially_product_in_sheet_product_in_date'=>'入库日期',
            'wms_partially_product_in_sheet_status_name'=>'入库状态',
            'wmsPartiallyProductInSheetQualityCheckNumber'=>'质检号',
        ];

        $options = [
            'creator'=>'中国汉广集团IT信息中心',
            'last_modified_by'=>'中国汉广集团IT信息中心',
            'title'=>$excel_name,
            'subject'=>$excel_name,
            'description'=>'半成品入库单',
            'keywords'=>'半成品入库单',
            'category'=>'半成品入库单',
            'summary'=>[
                'wms_partially_product_in_sheet_number'=>false,
                'common_producer_herb_info_name_product'=>false,
                'common_producer_herb_grade_name_product'=>false,
                'common_producer_herb_place_info_name'=>false,
                'piece_type_name'=>false,
                'wms_partially_product_in_sheet_weight_per_package'=>false,
                'wms_partially_product_in_sheet_package_number'=>true,
                'wms_partially_product_in_sheet_in_weight'=>true,
                'surplus_weight'=>true,
                'wms_partially_product_in_sheet_product_in_date'=>false,
                'wms_partially_product_in_sheet_status_name'=>false,
                'wmsPartiallyProductInSheetQualityCheckNumber'=>false,
            ]
        ];

        $style_options = [
            'h_align'=>[
                'wms_partially_product_in_sheet_number'=>'left',
                'common_producer_herb_info_name_product'=>'center',
                'common_producer_herb_grade_name_product'=>'center',
                'common_producer_herb_place_info_name'=>'center',
                'piece_type_name'=>'center',
                'wms_partially_product_in_sheet_weight_per_package'=>'right',
                'wms_partially_product_in_sheet_package_number'=>'right',
                'wms_partially_product_in_sheet_in_weight'=>'right',
                'surplus_weight'=>'right',
                'wms_partially_product_in_sheet_product_in_date'=>'center',
                'wms_partially_product_in_sheet_status_name'=>'center',
                'wmsPartiallyProductInSheetQualityCheckNumber'=>'center',
            ]
        ];

        //调用导出工具类进行导出
        \core\components\MyExcelHelper::array2excel($data, $excel_name, $headers, $options, $style_options);
    }
}

(3)控制器行为:

public function actionExport($serialized_model){
        $wmsPartiallyProductInSheetSearch = unserialize($serialized_model);
        $wmsPartiallyProductInSheetExport = new \backend\models\WmsPartiallyProductInSheetExport($wmsPartiallyProductInSheetSearch);
        $wmsPartiallyProductInSheetExport->export();
    }

优点:

(1)自己实现导出工具类,容易掌控代码,实现了封装的思想

(2)使用导出适配器,进一步封装了导出逻辑