Laravel Maatwebsite-Excel 3.1 實現匯出匯入

denfer發表於2020-03-23

匯出匯入的功能是平時開發比較常見的功能了,Laravel Maatwebsite-Excel更新了3.x,這次就自己封裝一個簡單的通用版並記錄一下遇到的問題。

  • PHP: ^7.3.4

  • Laravel: ^5.7

composer require maatwebsite/excel

建立檔案
laravel Maatwebsite-Excel 3.1 使用記錄

然後在composer.json新增

laravel Maatwebsite-Excel 3.1 使用記錄
然後執行:

composer dump-autoload

export.php內

<?php
namespace App\Http\Libraries\ExcelUtil;


use Illuminate\Support\Collection;
use Maatwebsite\Excel\Concerns\FromCollection;
use Maatwebsite\Excel\Concerns\WithEvents;
use Maatwebsite\Excel\Concerns\WithHeadings;
use Maatwebsite\Excel\Events\AfterSheet;

class export implements FromCollection,WithHeadings, WithEvents
{
    protected $data;
    protected $headings;
    protected $sheetName;       //設定sheet頁名稱
    protected $columnWidth = [];//設定列寬       key:列  value:寬
    protected $rowHeight = [];  //設定行高       key:行  value:高
    protected $mergeCells = []; //合併單元格      key:第一個單元格  value:第二個單元格
    protected $font = [];       //設定字型       key:A1:K8  value:11
    protected $bold = [];       //設定粗體       key:A1:K8  value:true
    protected $background = []; //設定背景顏色    key:A1:K8  value:#F0F0F0F
    protected $vertical = [];   //設定定位       key:A1:K8  value:center
    protected $horizontal = []; //設定水平定位    key:A1:K8  value:center
    protected $wrapText = [];   //設定是否自動換行 key:A1:K8  value:bool


    //設定頁面屬性時如果無效   更改excel格式嘗試即可

    //建構函式傳值
    public function __construct($data, $headings)
    {
        $this->data = $data;
        $this->headings = $headings;
        $this->createData();
    }

    public function headings(): array
    {
        return $this->headings;
    }

    //陣列轉集合
    public function collection()
    {
        return new Collection($this->data);
    }
    //業務程式碼
    public function createData()
    {
        $this->data = collect($this->data)->toArray();
    }

    public function registerEvents(): array
    {
        return [
            AfterSheet::class  => function(AfterSheet $event) {
                //設定sheet頁名稱
                $event->getSheet()->getDelegate()->setTitle($this->sheetName);

                //設定自動換行
                foreach ($this->wrapText as $column => $bool) {
                    $event->sheet->getDelegate()
                        ->getStyle($column)
                        ->getAlignment()
                        ->setWrapText(true);
                }
                //設定列寬
                foreach ($this->columnWidth as $column => $width) {
                    $event->sheet->getDelegate()
                        ->getColumnDimension($column)
                        ->setWidth($width);
                }
                //設定行高,$i為資料行數
                foreach ($this->rowHeight as $row => $height) {
                    $event->sheet->getDelegate()
                        ->getRowDimension($row)
                        ->setRowHeight($height);
                }

                //設定區域單元格垂直居中
                foreach ($this->vertical as $region => $position) {
                    $event->sheet->getDelegate()
                        ->getStyle($region)
                        ->getAlignment()
                        ->setVertical($position);
                }

                //設定區域單元格水平定位
                foreach ($this->horizontal as $region => $position) {
                    $event->sheet->getDelegate()
                        ->getStyle($region)
                        ->getAlignment()
                        ->setHorizontal($position);
                }

                //設定區域單元格字型
                foreach ($this->font as $region => $value) {
                    $event->sheet->getDelegate()
                        ->getStyle($region)
                        ->getFont()
                        ->setSize($value);
                }

                //設定區域單元格字型粗體
                foreach ($this->bold as $region => $bool) {
                    $event->sheet->getDelegate()
                        ->getStyle($region)
                        ->getFont()
                        ->setBold($bool);
                }


                //設定區域單元格背景顏色
                foreach ($this->background as $region => $item) {
                    $event->sheet->getDelegate()->getStyle($region)->applyFromArray([
                        'fill' => [
                            'fillType' => 'linear', //線性填充,類似漸變
                            'startColor' => [
                                'rgb' => $item //初始顏色
                            ],
                            //結束顏色,如果需要單一背景色,請和初始顏色保持一致
                            'endColor' => [
                                'argb' => $item
                            ]
                        ]
                    ]);
                }
                //合併單元格
                foreach ($this->mergeCells as $start => $end) {
                    $event->sheet->getDelegate()->mergeCells($start.':'.$end);
                }

            }
        ];
    }

    /**
     * @return array
     * @2020/3/22 10:33
     */
    public function setSheetName(string $sheetName)
    {
        $this->sheetName = trim($sheetName);
    }

    /**
     * @return array
     * @2020/3/22 10:33
     * [
     *    A1:K7 => true
     * ]
     */
    public function setWrapText (array $wrapText)
    {
        $this->wrapText = array_change_key_case($this->symbol($wrapText),CASE_UPPER);
    }

    /**
     * @return array
     * @2020/3/22 10:33
     * [
     *    A1:K7 => center
     * ]
     */
    public function setVertical (array $vertical)
    {
        $this->vertical = array_change_key_case($this->symbol($vertical),CASE_UPPER);
    }

    /**
     * @return array
     * @2020/3/22 10:33
     * [
     *    A1:K7 => center
     * ]
     */
    public function setHorizontal (array $horizontal)
    {
        $this->horizontal = array_change_key_case($this->symbol($horizontal),CASE_UPPER);
    }


    /**
     * @return array
     * @2020/3/22 10:33
     * [
     *    'B' => 40,
     *    'C' => 60
     * ]
     */
    public function setColumnWidth (array $columnwidth)
    {
        $this->columnWidth = array_change_key_case($columnwidth, CASE_UPPER);
    }

    /**
     * @return array
     * @2020/3/22 10:33
     * [
     *    1 => 40,
     *    2 => 60
     * ]
     */
    public function setRowHeight (array $rowHeight)
    {
        $this->rowHeight = $rowHeight;
    }

    /**
     * @return array
     * @2020/3/22 10:33
     * [
     *    A1:K7 => 12
     * ]
     */
    public function setFont (array $fount)
    {
        $this->font = array_change_key_case($fount, CASE_UPPER);
    }

    /**
     * @return array
     * @2020/3/22 10:33
     * [
     *    A1:K7 => true
     * ]
     */
    public function setBold (array $bold)
    {
        $this->bold = array_change_key_case($bold, CASE_UPPER);
    }

    /**
     * @return array
     * @2020/3/22 10:33
     * [
     *    A1:K7 => F0FF0F
     * ]
     */
    public function setBackground (array $background)
    {
        $this->background = array_change_key_case($background, CASE_UPPER);
    }


    public function symbol ($param) {
        $record = [];
        $count = count($this->data) + 1;//加上表頭
        foreach ($param as $key => $value) {
            $str = str_replace("*", $count, $key);
            $record[$str] = $value;
        }
        return $record;
    }
}

在控制器內呼叫。

use App\Http\Libraries\ExcelUtil\export;
use Maatwebsite\Excel\Facades\Excel;
public function export () {
         $data = [];//要匯出的資料
        $header = [];//匯出頭   例如:["名稱", "價格", "數量"]
        //匯出的頭和匯出的資料的長度要一致
        $excel = new export($data, $header);
        //從上面的封裝類中可以看到都有哪些屬性可以設定,下面是一些例子,大寫字母是列,字母后面的數字是行,支援*號設定整行或整列。
        //例如表頭是["名稱", "價格", "數量"],就有下面的設定
        $excel->setColumnWidth(['A' => 40,'B' => 40, 'C' => 40]);//設定單元格寬度
        $excel->setRowHeight([1 => 40]);//設定行高
        $excel->setFont(['A1:C1' => 12]);//設定字型大小
        $excel->setBold(['A1:C1' => true]);//設定字型粗細
        $excel->setBackground(['A1:C1' => '808080']);//設定背景色
        return Excel::download($excel, '匯出的檔名.xlsx');
}

根據自己的需求可設定行、列、字型、背景顏色等;
樣式在有的excel格式上不起作用,具體不知道為什麼,沒有再去研究了;
數字過大時展示科學計數法問題,我借鑑了一個大佬的方法

Laravel使用excel3.1匯出時,防止長數字變科學計數法的方法

開啟預設配置檔案vendor/maatwebsite/excel/src/DefaultValueBinder.php,新增

use PhpOffice\PhpSpreadsheet\Cell\DataType;

函式內部新增:

//超過10位的數字轉文字格式,防止科學計數法
if (strlen($value) > 10) {
    $cell->setValueExplicit($value, DataType::TYPE_STRING);
    return true;
}

檔案程式碼最終如下:

namespace Maatwebsite\Excel;
use PhpOffice\PhpSpreadsheet\Cell\Cell;
use PhpOffice\PhpSpreadsheet\Cell\DefaultValueBinder as PhpSpreadsheetDefaultValueBinder;
use PhpOffice\PhpSpreadsheet\Cell\DataType;
class DefaultValueBinder extends PhpSpreadsheetDefaultValueBinder
{
    /**
     * @param Cell $cell Cell to bind value to
     * @param mixed $value Value to bind in cell
     *
     * @return bool
     */
    public function bindValue(Cell $cell, $value)
    {
        if (is_array($value)) {
            $value = \json_encode($value);
        }
        //超過10位的數字轉文字格式,防止科學計數法
        if (strlen($value) > 10) {
            $cell->setValueExplicit($value, DataType::TYPE_STRING);
            return true;
        }
        return parent::bindValue($cell, $value);
    }
}

如果要把匯出檔案先下載到伺服器上可以使用store方法

import.php內

<?php

namespace App\Http\Libraries\ExcelUtil;


use Illuminate\Support\Collection;
use Maatwebsite\Excel\Concerns\ToCollection;

class import implements ToCollection
{
    public $data;
    protected $delTitle;

    /**
     *
     * @param $title integer   //去掉幾行標題  預設一行
     */
    public function __construct($delTitle = 1)
    {
        $this->delTitle = $delTitle;
    }

    /**
     * @param Collection $rows
     * @2020/3/23 9:53
     */
    public function collection(Collection $rows)
    {
        $this->delTitle($rows);
        //$rows 是陣列格式
        $this->data = $rows;
    }

    public function delTitle (&$rows) {
        $rows = $rows->slice($this->delTitle)->values();
    }
}

控制器內呼叫

public function import (Request $request) {
  $path = $request->file('file');
  $delTitle = 1;//指定頭行數  刪除它
  $excel = new import($delTitle);
  Excel::import($excel, $path->getRealPath());
  dd($excel->data); //這個地方就可以獲取到檔案內的集合了

  //在這可以驗證資料  入庫操作
}

程式碼都是貼上就可以使用的,記錄完畢

本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章