Home  >  Article  >  Backend Development  >  Method to export excel table in YII2 framework

Method to export excel table in YII2 framework

不言
不言Original
2018-06-07 10:25:341736browse

Recently I have been studying the Yii framework of PHP and I like it very much. When I encountered the problem of exporting Excel, I studied it and came up with the following article. This article mainly introduces you to the export of excel tables in the YII2 framework. The information is introduced in great detail through sample code. Friends in need can refer to it. Let’s take a look together.

Preface

The import and export of tables is a function we often encounter in daily development, and I happened to do it in a recent project Let’s talk about the table output function, and I have done it before when using TP, so I thought I would take advantage of this opportunity to sort it out when the functions are more diverse, so that it can be conveniently used when needed in the future, or for friends in need to refer to and study. I won’t say anything else below. Enough to say, let’s take a look at the detailed introduction:

This article is developed based on the YII2 framework. Different frameworks may need to be changed

1. Ordinary excel format Table output

First is the most common table exported in .xls format. First, let’s take a look at the display effect of the table on the website

Here you can see that the entire table has a total of 7 columns. Let’s look at the implementation of the code.

1.controller file

//导出统计

public function actionStatistics(){
 //设置内存
 ini_set("memory_limit", "2048M");
 set_time_limit(0);

 //获取用户ID
 $id = Yii::$app->user->identity->getId();

 //去用户表获取用户信息
 $user = Employee::find()->where(['id'=>$id])->one();

 //获取传过来的信息(时间,公司ID之类的,根据需要查询资料生成表格)
 $params = Yii::$app->request->get();
 $objectPHPExcel = new \PHPExcel();

 //设置表格头的输出
 $objectPHPExcel->setActiveSheetIndex()->setCellValue('A1', '代理公司');
 $objectPHPExcel->setActiveSheetIndex()->setCellValue('B1', '收入');
 $objectPHPExcel->setActiveSheetIndex()->setCellValue('C1', '成本');
 $objectPHPExcel->setActiveSheetIndex()->setCellValue('D1', '稿件数');
 $objectPHPExcel->setActiveSheetIndex()->setCellValue('E1', '毛利(收入-成本)');
 $objectPHPExcel->setActiveSheetIndex()->setCellValue('F1', '毛利率(毛利/收入)*100%');
 $objectPHPExcel->setActiveSheetIndex()->setCellValue('G1', 'ARPU值');

 //跳转到recharge这个model文件的statistics方法去处理数据
 $data = Recharge::statistics($params);

 //指定开始输出数据的行数
 $n = 2;
 foreach ($data as $v){
 $objectPHPExcel->getActiveSheet()->setCellValue('A'.($n) ,$v['company_name']);
 $objectPHPExcel->getActiveSheet()->setCellValue('B'.($n) ,$v['company_cost']);
 $objectPHPExcel->getActiveSheet()->setCellValue('C'.($n) ,$v['cost']);
 $objectPHPExcel->getActiveSheet()->setCellValue('D'.($n) ,$v['num']);
 $objectPHPExcel->getActiveSheet()->setCellValue('E'.($n) ,$v['gross_margin']);
 $objectPHPExcel->getActiveSheet()->setCellValue('F'.($n) ,$v['gross_profit_rate']);
 $objectPHPExcel->getActiveSheet()->setCellValue('G'.($n) ,$v['arpu']);
 $n = $n +1;
 }
 ob_end_clean();
 ob_start();
 header('Content-Type : application/vnd.ms-excel');

 //设置输出文件名及格式
 header('Content-Disposition:attachment;filename="代理公司统计'.date("YmdHis").'.xls"');

 //导出.xls格式的话使用Excel5,若是想导出.xlsx需要使用Excel2007
 $objWriter= \PHPExcel_IOFactory::createWriter($objectPHPExcel,'Excel5');
 $objWriter->save('php://output');
 ob_end_flush();

 //清空数据缓存
 unset($data);
}

2.model file

 <?php
 namespace app\models;//model层的命名空间
 //注意要引用yii的arrayhelper
 use yii\helpers\ArrayHelper;
 use Yii;
 class Recharge extends \yii\db\ActiveRecord
 {
 //excel一次导出条数
 const EXCEL_SIZE = 10000;
 
 //统计导出
 public static function statistics($params){

 //导出时间条件
 if(empty($params[&#39;min&#39;])){
 $date_max = date("Y-m-d",strtotime("-1 day"));
 $date_min = date("Y-m-d",strtotime("-31 day"));
 }else{
 $date_min = $params[&#39;min&#39;];
 $date_max = $params[&#39;max&#39;];
 }
 $where = &#39;&#39;;
 $where .= &#39;(`issue_date` BETWEEN &#39;.&#39;\&#39;&#39;.$date_min.&#39;\&#39;&#39;.&#39; AND &#39;.&#39;\&#39;&#39;.$date_max.&#39;\&#39;)&#39;;

 //查找指定数据
 $sql = &#39;select
 article.company_id,
 article.cost,
 article.company_cost
 from article WHERE article.status=2 AND &#39;.$where;
 $article = Article::findBySql($sql)->asArray()->all();
 $article = ArrayHelper::index($article,null,&#39;company_id&#39;);
 $companys = [];

 foreach ($article as $key=>$v){
 if(empty($key)){
 continue;
 }else{
 $number = count($v);
 $company = Company::find()->where([&#39;id&#39;=>$key])->select(&#39;name&#39;)->one();
 $company_name = $company[&#39;name&#39;];
 $cost = 0;
 $company_cost = 0;
 foreach ($v as $n){
 $cost += $n[&#39;cost&#39;];
 $company_cost += $n[&#39;company_cost&#39;];
 }
 if($company_cost == 0){
 $company_cost =1;
 }

 //这里注意,数据的存储顺序要和输出的表格里的顺序一样
 $companys[] = [
 //公司名
 &#39;company_name&#39; => $company_name,

 //收入
 &#39;company_cost&#39; => $company_cost,

 //成本
 &#39;cost&#39; => $cost,

 //稿件数
 &#39;num&#39; => $number,

 //毛利
 &#39;gross_margin&#39; => $company_cost-$cost,

 //毛利率
 &#39;gross_profit_rate&#39; => round(($company_cost-$cost)/$company_cost*100,2).&#39;%&#39;,

 //ARPU值
 &#39;arpu&#39; => round($company_cost/$number,2),
 ];
 }
 }
 return $companys;
 }
}


Finally The exported effect (the cell size has been adjusted after exporting) can be seen to be basically the same as what is displayed on the web page.

2. Big data table export

At this time, the boss said, we can’t Only look at the total data, it is best to export the detailed data as well. Now that the boss has spoken, do it. I still followed the first method, but the result prompted me that php crashed. When I tried again, I found that the number of written bytes exceeded. Open the php configuration file php.ini

memory_limit = 128M

and find that the default memory has been given to 128M, which should be enough. So I opened the database and took a look, oh!

Querying and exporting nearly 830,000 pieces of data will cause problems! What should I do, so I Googled it and found that for exporting big data (more than 20,000 items), it is best to export it in .csv format. No nonsense, just go to the code

1.controller file

//导出清单

public function actionInventory(){
 ini_set("memory_limit", "2048M");
 set_time_limit(0);
 $id = Yii::$app->user->identity->getId();
 $user = Employee::find()->where([&#39;id&#39;=>$id])->one();
 $params = Yii::$app->request->get();
 
 //类似的,跳转到recharge这个model文件里的inventory方法去处理数据
 $data = Recharge::inventory($params);
 
 //设置导出的文件名
 $fileName = iconv(&#39;utf-8&#39;, &#39;gbk&#39;, &#39;代理商统计清单&#39;.date("Y-m-d"));
 
 //设置表头
 $headlist = array(&#39;代理商&#39;,&#39;文章ID&#39;,&#39;文章标题&#39;,&#39;媒体&#39;,&#39;统计时间范围&#39;,&#39;状态&#39;,&#39;创建时间&#39;,&#39;审核时间&#39;,&#39;发稿时间&#39;,&#39;退稿时间&#39;,&#39;财务状态&#39;,&#39;成本&#39;,&#39;销售额&#39;,&#39;是否是预收款媒体类型&#39;,&#39;订单类别&#39;);
 header(&#39;Content-Type: application/vnd.ms-excel&#39;);
 
 //指明导出的格式
 header(&#39;Content-Disposition: attachment;filename="&#39;.$fileName.&#39;.csv"&#39;);
 header(&#39;Cache-Control: max-age=0&#39;);
 
 //打开PHP文件句柄,php://output 表示直接输出到浏览器
 $fp = fopen(&#39;php://output&#39;, &#39;a&#39;);
 
 //输出Excel列名信息
 foreach ($headlist as $key => $value) {
 //CSV的Excel支持GBK编码,一定要转换,否则乱码
 $headlist[$key] = iconv(&#39;utf-8&#39;, &#39;gbk&#39;, $value);
 }
 
 //将数据通过fputcsv写到文件句柄
 fputcsv($fp, $headlist);
 
 //每隔$limit行,刷新一下输出buffer,不要太大,也不要太小
 $limit = 100000;
 
 //逐行取出数据,不浪费内存
 foreach ($data as $k => $v) {
 //刷新一下输出buffer,防止由于数据过多造成问题
 if ($k % $limit == 0 && $k!=0) {
 ob_flush();
 flush();
 }
 $row = $data[$k];
 foreach ($row as $key => $value) {
 $row[$key] = iconv(&#39;utf-8&#39;, &#39;gbk&#39;, $value);
 }
 fputcsv($fp, $row);
 }
}

2.model file (because I have too much to deal with this part, so I only Selected part of the code), in the part of querying data, because there is a lot of data to be checked, you can take a look at the article I wrote before about Mysql big data query processing

//Export list

public static function inventory($params){
 //统计时间范围
 if(!empty($params[&#39;min&#39;]) && !empty($params[&#39;max&#39;])){
 $ti = strtotime($params[&#39;max&#39;])+3600*24;
 $max = date(&#39;Y-m-d&#39;,$ti);
 $time = $params[&#39;min&#39;].&#39;-&#39;.$params[&#39;max&#39;];
 $date_min = $params[&#39;min&#39;];
 $date_max = $max;
 }else{
 $date_max = date(&#39;Y-m-d&#39;);
 $date_min = date(&#39;Y-m-d&#39;,strtotime("-31 day"));
 $time = $date_min.&#39;-&#39;.$date_max;
 }
 //查询数据
 if($params[&#39;state&#39;] == 1){
 $where = &#39;&#39;;
 $where .= &#39; AND (`issue_date` BETWEEN &#39;.&#39;\&#39;&#39;.$date_min.&#39;\&#39;&#39;.&#39; AND &#39;.&#39;\&#39;&#39;.$date_max.&#39;\&#39;)&#39;;
 $map = &#39;select
  company.name,
  article.id,
  article.title,
  media.media_name,
  article.status,
  article.created,
  article.audit_at,
  article.issue_date,
  article.back_date,
  article.finance_status,
  article.cost,
  article.company_cost,
  media.is_advance
  from article
  LEFT JOIN custom_package ON custom_package.id = article.custom_package_id
  LEFT JOIN `order` ON custom_package.order_id = `order`.`id`
  LEFT JOIN company ON company.id = article.company_id
  LEFT JOIN media ON media.id = article.media_id
  where article.status=2 and `order`.package=0&#39;.$where;
 //查找的第一部分数据,使用asArray方法可以使我们查找的结果直接形成数组的形式,没有其他多余的数据占空间(注意:我这里查找分三部分是因为我要查三种不同的数据)
 $list1 = Article::findBySql($map)->asArray()->all();
 $where2 = &#39;&#39;;
 $where2 .= &#39; AND (`issue_date` BETWEEN &#39;.&#39;\&#39;&#39;.$date_min.&#39;\&#39;&#39;.&#39; AND &#39;.&#39;\&#39;&#39;.$date_max.&#39;\&#39;)&#39;;
 $where2 .= &#39; AND (`back_date` > \&#39;&#39;.$date_max.&#39;\&#39;)&#39;;
 $map2 = &#39;select
  company.name,
  article.id,
  article.title,
  media.media_name,
  article.status,
  article.created,
  article.audit_at,
  article.issue_date,
  article.back_date,
  article.finance_status,
  article.cost,
  article.company_cost,
  media.is_advance
  from article
  LEFT JOIN custom_package ON custom_package.id = article.custom_package_id
  LEFT JOIN `order` ON custom_package.order_id = `order`.`id`
  LEFT JOIN company ON company.id = article.company_id
  LEFT JOIN media ON media.id = article.media_id
  where article.status=3 and `order`.package=0 &#39;.$where2;
 //查找的第二部分数据
 $list2 = Article::findBySql($map2)->asArray()->all();
 $where3 = &#39;&#39;;
 $where3 .= &#39; AND (`issue_date` BETWEEN &#39;.&#39;\&#39;&#39;.$date_min.&#39;\&#39;&#39;.&#39; AND &#39;.&#39;\&#39;&#39;.$date_max.&#39;\&#39;)&#39;;
 $map3 = &#39;select
  company.name,
  article.id,
  article.title,
  media.media_name,
  article.status,
  article.created,
  article.audit_at,
  article.issue_date,
  article.back_date,
  article.finance_status,
  article.cost,
  article.company_cost,
  media.is_advance
  from article
  LEFT JOIN custom_package ON custom_package.id = article.custom_package_id
  LEFT JOIN `order` ON custom_package.order_id = `order`.`id`
  LEFT JOIN company ON company.id = article.company_id
  LEFT JOIN media ON media.id = article.media_id
  where article.status=5 &#39;.$where3;
 //查找的第三部分数据
 $list3 = Article::findBySql($map3)->asArray()->all();
 $list4 = ArrayHelper::merge($list1,$list2);
 $list = ArrayHelper::merge($list4,$list3);
 }
 //把结果按照显示顺序存到返回的数组中
 if(!empty($list)){
 foreach ($list as $key => $value){
 //代理公司
 $inventory[$key][&#39;company_name&#39;] = $value[&#39;name&#39;];
 //文章ID
 $inventory[$key][&#39;id&#39;] = $value[&#39;id&#39;];
 //文章标题
 $inventory[$key][&#39;title&#39;] = $value[&#39;title&#39;];
 //媒体
 $inventory[$key][&#39;media&#39;] = $value[&#39;media_name&#39;];
 //统计时间
 $inventory[$key][&#39;time&#39;] = $time;
 //状态
 switch($value[&#39;status&#39;]){
 case 2:
  $inventory[$key][&#39;status&#39;] = &#39;已发布&#39;;
  break;
 case 3:
  $inventory[$key][&#39;status&#39;] = &#39;已退稿&#39;;
  break;
 case 5:
  $inventory[$key][&#39;status&#39;] = &#39;异常稿件&#39;;
  break;
 }
 //创建时间
 $inventory[$key][&#39;created&#39;] = $value[&#39;created&#39;];
 //审核时间
 $inventory[$key][&#39;audit&#39;] = $value[&#39;audit_at&#39;];
 //发稿时间
 $inventory[$key][&#39;issue_date&#39;] = $value[&#39;issue_date&#39;];
 //退稿时间
 $inventory[$key][&#39;back_date&#39;] = $value[&#39;back_date&#39;];
 //财务状态
 switch($value[&#39;finance_status&#39;]){
 case 0:
  $inventory[$key][&#39;finance_status&#39;] = &#39;未到结算期&#39;;
  break;
 case 1:
  $inventory[$key][&#39;finance_status&#39;] = &#39;可结算&#39;;
  break;
 case 2:
  $inventory[$key][&#39;finance_status&#39;] = &#39;资源审批中&#39;;
  break;
 case 3:
  $inventory[$key][&#39;finance_status&#39;] = &#39;财务审批中&#39;;
  break;
 case 4:
  $inventory[$key][&#39;finance_status&#39;] = &#39;已结款&#39;;
  break;
 case 5:
  $inventory[$key][&#39;finance_status&#39;] = &#39;未通过&#39;;
  break;
 case 6:
  $inventory[$key][&#39;finance_status&#39;] = &#39;财务已审批&#39;;
  break;
 }
 //成本
 $inventory[$key][&#39;cost&#39;] = $value[&#39;cost&#39;];
 //销售额
 $inventory[$key][&#39;company_cost&#39;] = $value[&#39;company_cost&#39;];
 //是否是预售
 switch($value[&#39;is_advance&#39;]){
 case 0:
  $inventory[$key][&#39;is_advance&#39;] = &#39;否&#39;;
  break;
 case 1:
  $inventory[$key][&#39;is_advance&#39;] = &#39;是&#39;;
  break;
 case 2:
  $inventory[$key][&#39;is_advance&#39;] = &#39;合同&#39;;
  break;
 }
 //订单类别
 switch($params[&#39;state&#39;]){
 case 1:
  $inventory[$key][&#39;order_type&#39;] = &#39;时间区间无退稿完成订单&#39;;
  break;
 case 2:
  $inventory[$key][&#39;order_type&#39;] = &#39;时间区间发布前退稿订单&#39;;
  break;
 case 3:
  $inventory[$key][&#39;order_type&#39;] = &#39;时间区间发布后时间区间退稿订单&#39;;
  break;
 case 4:
  $inventory[$key][&#39;order_type&#39;] = &#39;时间区间之前发布时间区间内退稿订单&#39;;
  break;
 case 5:
  $inventory[$key][&#39;order_type&#39;] = &#39;异常订单&#39;;
  break;
 }
 }
 }else{
 $inventory[0][&#39;company_name&#39;] = &#39;无数据导出&#39;;
 }
 return $inventory;
}

3. Export results

Export quantity

Export File

#Basically ensure that the entire process is completed within 2 to 4 seconds

3. Merge cells

The boss saw that you had done a good job and said that you also exported the recharge statistics. Thinking about it, I am someone who has dealt with so much data. What can be done in minutes? Come on, show me the prototype

Pfft, I’ve said it all, let’s do it. While doing it, I discovered that this export is mainly to solve the problem of cell merging. After checking the information, I found that PHP itself cannot achieve cell merging, so I plan to achieve it through phpexcel

If you use PHPExcel, the basic operation is like this (merge A1 to E1)

$objPHPExcel->getActiveSheet()->mergeCells(&#39;A1:E1&#39;);
// 表格填充内容
$objPHPExcel->getActiveSheet()->setCellValue(&#39;A1&#39;,&#39;The quick brown fox.&#39;);

Result

Or like this (merge A1 to E4)

$objPHPExcel->getActiveSheet()->mergeCells(&#39;A1:E4&#39;);
$objPHPExcel->getActiveSheet()->setCellValue(&#39;A1&#39;,&#39;The quick brown fox.&#39;);

Result

This does not meet my requirements. First of all, it is merged one by one. Secondly, the type under the recharge amount I want to display will change. It is impossible to fix it and then change it every time. So this method was abandoned.

Later, with the help of my friends, I tried to use html to transfer excel.

1. Method file (because I have to execute it regularly every day , so it was not written to the controller layer)

public function actionExcelRechargeStatistics(){

 //先定义一个excel文件
 $filename = date(&#39;【充值统计表】(&#39;.date(&#39;Y-m-d&#39;).&#39;导出)&#39;).".xls";
 header("Content-Type: application/vnd.ms-execl");
 header("Content-Type: application/vnd.ms-excel; charset=utf-8");
 header("Content-Disposition: attachment; filename=$filename");
 header("Pragma: no-cache");
 header("Expires: 0");
 //时间条件
 if(empty($params[&#39;min&#39;])){
 $time = date(&#39;Y-m-d&#39;,strtotime("+1 day"));
 $where = &#39; created < \&#39; &#39;.$time.&#39;\&#39;&#39;;
 }else{
 $time = $params[&#39;min&#39;]+3600*24;
 $time_end = $params[&#39;max&#39;]+3600*24;
 $where = &#39; created <= \&#39; &#39;.$time_end.&#39;\&#39; AND created >= \&#39;&#39;.$time.&#39;\&#39; &#39;;
 }
 //充值类型列表
 $recharge_type = Recharge::find()->asArray()->all();
 if(empty($recharge_type)){
 $rechargelist[0]= &#39;&#39;;
 }else{
 $rechargelist = ArrayHelper::map($recharge_type,&#39;id&#39;,&#39;recharge_name&#39;);
 }
 $rechargelist1 = $rechargelist;
 $count = count($rechargelist1);
 //使用html语句生成显示的格式
 $excel_content = &#39;<meta http-equiv="content-type" content="application/ms-excel; charset=utf-8"/>&#39;;
 $excel_content .= &#39;<table border="1" style="font-size:14px;">&#39;;
 $excel_content .= &#39;<thead>
   <tr>
   <th rowspan="2">ID</th>
   <th rowspan="2">公司名称</th>
   <th colspan=&#39;.$count.&#39;>充值金额</th>
   <th rowspan="2">充值大小</th>
   <th rowspan="2">实际消费</th>
   <th rowspan="2">当前余额</th>
   </tr>
   <tr>
  &#39;;
 foreach ($rechargelist1 as $v => $t){
 $excel_content .= &#39;<th colspan="1">&#39;.$t.&#39;</th>&#39;;
 }
 $excel_content .= &#39;</tr>
  </thead>&#39;;
 //查找最新的固化数据
 $search = RechargeStatistics::find()->where($where)->asArray()->all();
 if(!empty($search)){
 foreach ($search as $key => $value){
 $search[$key][&#39;recharge&#39;] = unserialize($value[&#39;recharge&#39;]);
 }
 }
 //html语句填充数据
 if(empty($search)){
 }else{
 foreach ($search as $k) {
 $excel_content .= &#39;<td>&#39;.$k[&#39;company_id&#39;].&#39;</td>&#39;;
 $excel_content .= &#39;<td>&#39;.$k[&#39;company_name&#39;].&#39;</td>&#39;;
 foreach ($rechargelist1 as $v=>$t){
 $price = 0;
 foreach ($k[&#39;recharge&#39;] as $q=>$w){
  if($w[&#39;recharge_id&#39;] == $v){
  $price = $w[&#39;price&#39;];
  break;
  }
 }
 $excel_content .= &#39;<td>&#39;.$price.&#39;</td>&#39;;
 }
 $excel_content .= &#39;<td>&#39;.$k[&#39;total&#39;].&#39;</td>&#39;;
 $excel_content .= &#39;<td>&#39;.$k[&#39;consume&#39;].&#39;</td>&#39;;
 $excel_content .= &#39;<td>&#39;.($k[&#39;total&#39;]-$k[&#39;consume&#39;]).&#39;</td></tr>&#39;;
 }
 }
 $excel_content .= &#39;</table>&#39;;
 echo $excel_content;
 die;
}

2. Result

Basically all the tasks have been completed here!

Related recommendations:

memcache usage examples in Yii framework

The above is the detailed content of Method to export excel table in YII2 framework. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn