匯入 Excel 或是 csv 檔案到 Laravel
使用 Laravel-excel 套件匯入 Excel 或是 csv 檔案,並寫入資料庫
在後台介面尚未建置完成,或是使用者有大量資料不想逐筆新增
而是要透過表單一次性匯入,這時候我們就會用到 Laravel-excel 這個套件
同時也分享一次匯入大量資料的時候遇到的坑
目前大部分開發者使用的套件都是:Laravel-excel
支援大多數匯入時候會使用的檔案格式,並且包含很多常用功能
官方文件寫的也是清楚並且有範例程式碼
安裝方式
使用指令安裝,會透過 auto-discovered 註冊進 ServiceProvider 和 Facade
目前最新版本是:Documentation 3.1
composer require maatwebsite/excel
如果需要加入自定義的 config 就執行指令產生config/excel.php
composer require maatwebsite/excel
php artisan vendor:publish --provider="Maatwebsite\Excel\ExcelServiceProvider" --tag=config
定義檔案讀入格式
因為 Laravel 自帶的 collection 類型太好用
所以我們也想把讀取到的表單資料轉成 collection 格式
首先要先建立資料要載入之後的物件定義,我們假設要匯入的是商品規格的 csv
所以就建立一個名為app/Imports/ProductImport.php
的檔案
<?php
namespace App\Imports;
use Illuminate\Support\Collection;
use Maatwebsite\Excel\Concerns\ToCollection;
class ProductImport implements ToCollection
{
public $collection;
public function collection(Collection $collection)
{
$this->collection = $collection->transform(function ($row) {
return collect([
'name' => $row[1],
'main_image' => $row[2],
'author_name' => $row[3],
'material' => $row[4],
'specification' => $row[5],
'description' => $row[6],
]);
});
}
}
可以看到我們使用了ToCollection
這個 interface
並且定義了collection
和讀取到的表單內容對應
讀取 csv 檔案
在 controller 呼叫當初藉由 auto-discovered 註冊到 Facade 的Excel
來讀取上傳的檔案
首先是建立物件ProductImport
,接著透過 Facade 呼叫Excel
套件並使用載入功能
載入之後透過 collection 的 shift() 方法去除第一列的 header 資料
public function uploadCsv(Request $request)
{
$import = new ProductImport();
Excel::import($import, $request->file('file'));
$productCollection = $import->collection;
$productCollection->shift();
}
如此就可以將上傳的 csv 檔案載入成 collection 來做後續操作
上傳測試
用 Postman 要測試,只要在 Body 頁籤選擇form-data
類性之後
key
可以名稱的後面下拉選單可以選擇 file 格式
常見問題
413 Payload Too Large
這個是來自 Nginx 的錯誤訊息,意思是:上傳的檔案太大了,Server 端忍不住
參考:413 Payload Too Large
解決方法以 PHP 開發環境為例
Nginx config 調整
預設是 2M,如果要傳超過的檔案就必須調整
打開nginx.conf
在 server 段落內加上
client_max_body_size 10M;
如此一來請求才會傳入 nginx 內交給 php 執行後續內容
php.ini 調整
php.ini 是 php 的各項設定
其中我們要修改的就是upload_max_filesize
這項,預設是 2M
將其設定的跟 nginx 一樣
upload_max_filesize = 10M;
如果是使用 Docker 建立環境,則在建立客製的php.ini
檔案之後
將其掛載到容器內指定位置即可
- ./php/config/php.ini:/usr/local/etc/php/conf.d/php.ini
以上調整都完成之後,對有調整的容器下指令重啟或是重新載入設定檔
並進入容器內確認設定都有被載入
PHP 就用 phpinfo()
、nginx 就用 nginx -T
Error: 1390 too many placeholders
當你成功把檔案上傳,並且開始執行程式
在途中把資料印出來檢查都照你預期的方式執行
於是你開始使用 query builder 組裝要執行的 SQL 語句
突然!報出一條錯誤訊息
1390 Prepared statement contains too many placeholders
這個是來自於 MySQL 的報錯,因為我們使用了:Prepared statement
而 Prepared statement 只支援 65535 (2^16-1) 個參數設定
如果你今天想要一口氣寫入超級多資料,不論是欄位還是筆數
只要超過這個數字,就會無法組裝進而報錯
知道發生的原因之後就很好想出解決辦法了!
那就是不要一次寫入太多資料…廢話
言歸正傳,想要解決這個問題我們可以先將很大量的資料
比如說內含一萬筆商品內容的陣列切分成 500 筆一組的的多個陣列
一次執行 500 筆資料的寫入或修改,如此一來就可以避免 too many placeholders 的問題
在 PHP 中我們會使用 array_chunk 來分割
$productChunks = array_chunk($products, 500);
執行完之後會得到一個把原始資料每 500 筆切為一包的新陣列
接著就可以用迴圈把每一組的 500 筆取出來執行
原本是一個迴圈跑 10,000 筆資料,現在是一次 500 筆跑 20 次
這就是使用 Laravel-excel 套件來讀取 csv 並且寫入資料庫的方法
至於匯出檔案就留到下次有實際案例再來說明