項目裏需要導入了,正好 3.1 版本,也支持導入了,補充下 excel 導入的翻譯文檔。(工作比較忙,大概過了一遍,可能有不少錯誤的地方)
1.5分鐘快速入門
在 app/Import 創建一個導入類
php artisan make:import UsersImport --model=User
會在 app/Imports 發現新創建的文件:
.
├── app
│ ├── Imports
│ │ ├── UsersImport.php
│
└── composer.json
如果你更喜歡手動創建導入,可以在 app/Imports 下創建如下文件:
<?php
namespace App\Imports;
use App\User;
use Illuminate\Support\Facades\Hash;
use Maatwebsite\Excel\Concerns\ToModel;
class UsersImport implements ToModel
{
/**
* @param array $row
*
* @return User|null
*/
public function model(array $row)
{
return new User([
'name' => $row[0],
'email' => $row[1],
'password' => Hash::make($row[2]),
]);
}
}
在控制器中調用導入:
use App\Imports\UsersImport;
use Maatwebsite\Excel\Facades\Excel;
use App\Http\Controllers\Controller;
class UsersController extends Controller
{
public function import()
{
Excel::import(new UsersImport, 'users.xlsx');
return redirect('/')->with('success', 'All good!');
}
}
2.基本導入
如果已經按 5 分鐘快速入門練習,我們已經有了一個 UsersImport 類。
...
從默認文件系統導入:
將 UsersImport 傳遞給 Excel::import() 方法,將告知包如何導入第二個參數傳遞的 excel 文件。導入的 excel 文件需要位於我們的默認文件系統(查看 config/filesystems.php)。
Excel::import(new UsersImport, 'users.xlsx');
從另一個文件系統導入:
我們可以通過傳遞第三個參數,來指定另一個文件系統,例如:Amazon s3 文件系統
Excel::import(new UsersImport, 'users.xlsx', 's3');
導入上傳文件:
如果讓用戶上傳 excel 文件,我們也可以直接傳遞上傳的文件
Excel::import(new UsersImport, request()->file('excel.xls'));
導入全路徑:
如果要指定文件所在的路徑,而不必將其移動到文件系統,我們可以直接將該文件路徑傳遞給 import 方法
Excel::import(new UsersImport, storage_path('users.xlsx'));
導入爲數組或集合:
如果想要省略 ToArray 或 ToCollection concern,並且希望在控制中得到一個導入數據的數組(注意性能),可以使用 ::toArray() 或 ::toCollection() 方法
$array = Excel::toArray(new UsersImport, 'users.xlsx');
$collection = Excel::toCollection(new UsersImport, 'users.xlsx');
指定 'reader' 類型:
如果通過文件擴展名無法檢測到 reader 類型,我們可以傳遞第 4 個參數來指定一個 reader 類型
Excel::import(new UsersImport, 'users.xlsx', 's3', \Maatwebsite\Excel\Excel::XLSX);
3.導入到集合
開始導入的最簡單方式是,創建一個自定義的導入類。我們將使用一個 UsersImport 導入類作爲示例。
在 App/Imports 下創建一個新的 UsersImport 導入類:
namespace App\Imports;
use App\User;
use Illuminate\Support\Collection;
use Maatwebsite\Excel\Concerns\ToCollection;
class UsersImport implements ToCollection
{
public function collection(Collection $rows)
{
foreach ($rows as $row)
{
User::create([
'name' => $row[0],
]);
}
}
}
collection() 方法接收一個行集合。每一行是一個由單元格的值填充的數組。
如果文件有多個工作表,collection() 方法將會被調用多次。
在控制器中,現在我們可以像下面這樣導入:
public function import()
{
Excel::import(new UsersImport, 'users.xlsx');
}
警告:
不管我們在 collection() 方法中返回什麼內容,都不會返回給控制器
4.導入到模型
如果我們想要將一個工作簿導入到一個 Eloquent 模型,可以使用 ToModel concern。ToModel concern 將強制 model() 方法返回一個 model 類型
namespace App\Imports;
use App\User;
use Maatwebsite\Excel\Concerns\ToModel;
class UsersImport implements ToModel
{
public function model(array $row)
{
return new User([
'name' => $row[0],
]);
}
}
返回的模型將爲我們保存。每行將導致(至少)一次保存,並且還將觸發模型事件。
警告:
當使用 ToModel 時,我們不應該自己保存模型,因爲這將破壞批量插入的功能。如果我們需要自己保存模型,可以考慮使用 OnEachRow
跳過行
如果想要跳過行,可以返回 null
public function model(array $row)
{
if (!isset($row[0])) {
return null;
}
return new User([
'name' => $row[0],
]);
}
可能的列名(導入一個列,可嘗試幾個字段,直到給定的列存在值。例如:用戶名:username -> nick_name -> mobile)
如果想要通過幾個可能的列名導入行(使用 WithHeadingRow),可以使用 null 合併操作符(??)。如果 first name 的列存在(示例中是 client_name),並且不是 Null,則返回它的值;否則尋找第二個可能的列名(示例中是 client)。
public function model(array $row) {
return new User([
'name' => $row['client_name'] ?? $row['client'] ?? $row['name'] ?? null
]);
}
自己來處理持久化
在某些情況下,我們可能並沒有一個導入,該導入的每一行都是一個 Eloquent 模型,並且我們想要更多的控制發生了什麼。在這些情況下,我們可以使用 OnEachRow concern
namespace App\Imports;
use App\User;
use Maatwebsite\Excel\Row;
use Maatwebsite\Excel\Concerns\OnEachRow;
class UsersImport implements OnEachRow
{
public function onRow(Row $row)
{
$rowIndex = $row->getIndex();
$row = $row->toArray();
$group = Group::firstOrCreate([
'name' => $row[1],
]);
$group->users()->create([
'name' => $row[0],
]);
}
}
警告:
當使用 OnEachRow,我們不能使用批量插入,因爲該模型已經在 onRow 方法中持久化的保存。
5.可導入
在之前的示例中,我們使用 Excel::import facade 來開始一個導入。
Laravel Excel 也提供了一個 'Maatwebsite\Excel\Concerns\Importable' trait,來使得導入類可導入。
namespace App\Imports;
use App\User;
use Maatwebsite\Excel\Concerns\ToModel;
use Maatwebsite\Excel\Concerns\Importable;
class UsersImport implements ToModel
{
use Importable;
public function model(array $row)
{
return new User([
'name' => $row[0],
]);
}
}
導入
我們現在可以不需要 facade 來導入:
(new UsersImport)->import('users.xlsx', 'local', \Maatwebsite\Excel\Excel::XLSX);
隊列
或隊列導入:
(new UsersImport)->queue('users.xlsx');
導入到數組
導入可以加載到數組:
$array = (new UsersImport)->toArray('users.xlsx');
導入到集合
導入可以加載到集合:
$collection = (new UsersImport)->toCollection('users.xlsx');
6.導入格式
默認情況下,導入格式由文件的擴展名決定。如果要顯式配置導入格式,可以將其作爲第 3 個參數傳遞。
XLSX
(new UsersImport)->import('users.xlsx', null, \Maatwebsite\Excel\Excel::XLSX);
CSV
(new UsersImport)->import('users.csv', null, \Maatwebsite\Excel\Excel::CSV);
TSV
(new UsersImport)->import('users.tsv', null, \Maatwebsite\Excel\Excel::TSV);
ODS
(new UsersImport)->import('users.ods', null, \Maatwebsite\Excel\Excel::ODS);
XLS
(new UsersImport)->import('users.xls', null, \Maatwebsite\Excel\Excel::XLS);
SLK
(new UsersImport)->import('users.slk', null, \Maatwebsite\Excel\Excel::SLK);
XML
(new UsersImport)->import('users.xml', null, \Maatwebsite\Excel\Excel::XML);
GUNMERIC
(new UsersImport)->import('users.gnumeric', null, \Maatwebsite\Excel\Excel::GNUMERIC);
HTML
(new UsersImport)->import('users.html', null, \Maatwebsite\Excel\Excel::HTML);
7.多個工作表
當一個文件有多個工作表時,每個工作表都將通過導入對象。如果想要單獨處理每個工作表,需要實現 WithMultipleSheets concern。
sheets() 方法希望返回一個 '工作表導入對象' 的數組。工作表的順序很重要,數組中第一個工作表導入對象,將自動鏈接到 Excel 文件的第一個工作表。
namespace App\Imports;
use Maatwebsite\Excel\Concerns\WithMultipleSheets;
class UsersImport implements WithMultipleSheets
{
public function sheets(): array
{
return [
new FirstSheetImport()
];
}
}
工作表導入類可以導入 '與普通導入對象相同的' concerns
namespace App\Imports;
use Illuminate\Support\Collection;
use Maatwebsite\Excel\Concerns\ToCollection;
class FirstSheetImport implements ToCollection
{
public function collection(Collection $rows)
{
//
}
}
通過工作表索引選擇工作表
如果想要更多地控制,選擇哪些工作表,以及如何將它們映射到指定的工作表導入對象,我們可以使用工作表索引作爲下標。工作表索引從 0 開始。
namespace App\Imports;
use Maatwebsite\Excel\Concerns\WithMultipleSheets;
class UsersImport implements WithMultipleSheets
{
public function sheets(): array
{
return [
0 => new FirstSheetImport(),
1 => new SecondSheetImport(),
];
}
}
通過工作表名稱選擇工作表
如果我們只知道工作表名稱,而不知道工作表索引,我們也可以使用工作表名稱作爲選擇器。將工作表名稱作爲數組索引,來將工作表鏈接到工作表導入對象。
namespace App\Imports;
use Maatwebsite\Excel\Concerns\WithMultipleSheets;
class UsersImport implements WithMultipleSheets
{
public function sheets(): array
{
return [
'Worksheet 1' => new FirstSheetImport(),
'Worksheet 2' => new SecondSheetImport(),
];
}
}
警告:
未顯式定義在 sheet() 方法中的工作表,將被忽略,因此不會被導入。
跳過未知的工作表
當我們定義了不存在的工作表名稱或索引後,將拋出 Maatwebsite\Excel\Exceptions\SheetNotFoundException 異常
當工作表不存在時,我們不想拋出異常,可以使用 Maatwebsite\Excel\Concerns\SkipsUnknownSheets concern
namespace App\Imports;
use Maatwebsite\Excel\Concerns\WithMultipleSheets;
use Maatwebsite\Excel\Concerns\SkipsUnknownSheets;
class UsersImport implements WithMultipleSheets, SkipsUnknownSheets
{
public function sheets(): array
{
return [
'Worksheet 1' => new FirstSheetImport(),
'Worksheet 2' => new SecondSheetImport(),
];
}
public function onUnknownSheet($sheetName)
{
// 例如,我們可以記錄未找到工作表
info("Sheet {$sheetName} was skipped");
}
}
僅跳過指定的工作表
如果想要一個可選的工作表,並且其他的工作表仍然失敗,還可以讓工作表導入對象實現 SkipsUnknownSheets
namespace App\Imports;
use Maatwebsite\Excel\Concerns\SkipsUnknownSheets;
class FirstSheetImport implements SkipsUnknownSheets
{
public function onUnknownSheet($sheetName)
{
// 例如,我們可以記錄未找到工作表
info("Sheet {$sheetName} was skipped");
}
}
現在,只有 FirstSheetImport 在沒找到時,會被跳過。任意其他定義的工作表將被跳過(將報錯吧,這裏應該寫錯了)
有條件地加載工作表
如果要指定每個導入應該導入哪些工作表,可以使用 Maatwebsite\Excel\Concerns\WithConditionalSheets trait
namespace App\Imports;
use Maatwebsite\Excel\Concerns\WithMultipleSheets;
use Maatwebsite\Excel\Concerns\WithConditionalSheets;
class UsersImport implements WithMultipleSheets
{
use WithConditionalSheets;
public function conditionalSheets(): array
{
return [
'Worksheet 1' => new FirstSheetImport(),
'Worksheet 2' => new SecondSheetImport(),
'Worksheet 3' => new ThirdSheetImport(),
];
}
}
現在可以使用 onlySheets 方法來指定本次導入應該加載哪些工作表
$import = new UsersImport();
$import->onlySheets('Worksheet 1', 'Worksheet 3');
Excel::import($import, 'users.xlsx');
8.標題行
如果文件包含標題行,並且希望將這些名稱作爲每一行的數組的鍵,可以實現 WithHeadingRow concern
假設我們的 Excel 文件如下所示:
Name Email @Field
dongxuemin [email protected] 30
我們現在可以引用這些標題,來替代之前的數字格式的數組鍵
namespace App\Imports;
use App\User;
use Maatwebsite\Excel\Concerns\ToModel;
use Maatwebsite\Excel\Concerns\WithHeadingRow;
class UsersImport implements ToModel, WithHeadingRow
{
public function model(array $row)
{
return new User([
'name' => $row['name'],
'email' => $row['email'],
'at' => $row['at_field'],
]);
}
}
標題行在其他行(非第一行)
如果標題行不在第一行,可以在導入類中簡單地指定:
namespace App\Imports;
use App\User;
use Maatwebsite\Excel\Concerns\ToModel;
use Maatwebsite\Excel\Concerns\WithHeadingRow;
class UsersImport implements ToModel, WithHeadingRow
{
public function model(array $row)
{
return new User([
'name' => $row['name'],
'email' => $row['email'],
]);
}
public function headingRow(): int
{
return 2;
}
}
現在第二行將作爲標題行
標題鍵格式
默認情況下,標題鍵使用 Laravel 的 str_slug() 幫助函數進行格式化,例如,這意味着所有的空格被轉換爲 _
如果想要改變該默認行爲,可以通過擴展 HeadingRowFormatter 來實現。
不格式化
如果不需要格式化,可以使用 none 格式化程序。數組建將包含標題行中的確切數據。
use Maatwebsite\Excel\Imports\HeadingRowFormatter;
HeadingRowFormatter::default('none');
public function model(array $row)
{
return new User([
'name' => $row['Name'],
'email' => $row['Email'],
]);
}
自定義格式化程序
可以在服務提供者中,使用 ::extend() 來定義一個自定義格式化程序
HeadingRowFormatter::extend('custom', function($value) {
return 'do-something-custom' . $value;
});
可以在 config/excel.php 中設置自定義格式化程序
'imports' => [
'heading_row' => [
'formatter' => 'custom',
],
],
或者之後可以在服務提供者中設置新的格式化程序
HeadingRowFormatter::default('custom');
只導入標題行
有時我們可能希望預先獲取標題行來進行一些驗證,有一個簡單的快捷方式:HeadingRowImport
use Maatwebsite\Excel\HeadingRowImport;
class UsersImportController extends Controller
{
public function import()
{
$headings = (new HeadingRowImport)->toArray('users.xlsx');
}
}
標題數組包含每張工作表的一個標題數組
9.批量插入
將大文件導入到 Eloquent 模型,可能很快會有瓶頸,因爲每一行都會導致一次插入查詢。
使用 WithBatchInserts concern,我們可以通過指定一個批處理大小,來限制查詢數量。該批處理大小將決定一次將多少模型插入到數據庫。這將大大減少導入時間。
namespace App\Imports;
use App\User;
use Maatwebsite\Excel\Concerns\ToModel;
use Maatwebsite\Excel\Concerns\WithBatchInserts;
class UsersImport implements ToModel, WithBatchInserts
{
public function model(array $row)
{
return new User([
'name' => $row[0],
]);
}
public function batchSize(): int
{
return 1000;
}
}
ToModel
該 concern 只與 ToModel concern 一起工作時有效。
批處理大小
1000 的批處理大小,並不是導入的最佳情況。我們需要根據自己的系統,來找到最佳的數值。
10.分塊讀取
導入大文件,會佔用很大內存,因爲庫嘗試將整個工作表加載到內存。
要減少內存使用的增加,我們可以使用 WithChunkReading concern。這將以分塊的方式來讀取工作表,並控制內存使用量。
namespace App\Imports;
use App\User;
use Maatwebsite\Excel\Concerns\ToModel;
use Maatwebsite\Excel\Concerns\WithChunkReading;
class UsersImport implements ToModel, WithChunkReading
{
public function model(array $row)
{
return new User([
'name' => $row[0],
]);
}
public function chunkSize(): int
{
return 1000;
}
}
警告:
1000 的塊大小,並不是導入的最佳情況。我們需要根據自己的系統,來找到最佳的數值。
與批量插入一起使用
(考慮時間和內存的消耗)最完美的解決方案是,將批量插入和分塊讀取結合使用
namespace App\Imports;
use App\User;
use Maatwebsite\Excel\Concerns\ToModel;
use Maatwebsite\Excel\Concerns\WithBatchInserts;
use Maatwebsite\Excel\Concerns\WithChunkReading;
class UsersImport implements ToModel, WithBatchInserts, WithChunkReading
{
public function model(array $row)
{
return new User([
'name' => $row[0],
]);
}
public function batchSize(): int
{
return 1000;
}
public function chunkSize(): int
{
return 1000;
}
}
11.隊列讀取
隊列分塊讀取
當使用 WithChunkReading concern 時,我們也可以選擇將每個分塊放入到隊列中。只需簡單地添加 ShouldQueue contract 即可。
namespace App\Imports;
use App\User;
use Maatwebsite\Excel\Concerns\ToModel;
use Illuminate\Contracts\Queue\ShouldQueue;
use Maatwebsite\Excel\Concerns\WithChunkReading;
class UsersImport implements ToModel, WithChunkReading, ShouldQueue
{
public function model(array $row)
{
return new User([
'name' => $row[0],
]);
}
public function chunkSize(): int
{
return 1000;
}
}
現在,包含 1000 行的塊,將被放入到隊列
警告:
ShouldQueue 僅支持和 WithChunkReading 結合使用
顯式地隊列導入
通過使用 ::queueImport,我們可以顯式地隊列導入
Excel::queueImport(new UsersImport, 'users.xlsx');
當使用 Importable trait 時,我們可以使用 queue 方法:
(new UsersImport)->queue('users.xlsx');
警告:
ShouldQueue 總是需要的
隱式地隊列導入
當使用了 ShouldQueue,導入將被自動放入隊列
Excel::import(new UsersImport, 'users.xlsx');
追加任務(注意:這個任務,並不一定是導入任務,可以是任意的任務)
當隊列導入時,將返回 Laravel 的 PendingDispatch 實例。這意味着我們可以鏈式地將額外的任務添加到隊列的末尾,並且只有在所有導入任務正確執行完畢後,纔會執行追加的任務
(new UsersImport)->queue('users.xlsx')->chain([
new NotifyUserOfCompletedImport(request()->user()),
]);
namespace App\Jobs;
use App\User;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\SerializesModels;
class NotifyUserOfCompletedImport implements ShouldQueue
{
use Queueable, SerializesModels;
public $user;
public function __construct(User $user)
{
$this->user = $user;
}
public function handle()
{
$this->user->notify(new ImportReady());
}
}
自定義隊列
由於返回了 PendingDispatch,我們也可以改變使用的隊列
(new UsersImport)->queue('users.xlsx')->allOnQueue('imports')
多服務器設置
如果正在處理多服務器設置(例如:負載均衡),我們可能希望確保每個任務的臨時文件是相同的。我們可以在配置中,配置一個遠程的臨時文件來實現。
在 config/excel.php:
'temporary_files' => [
'remote_disk' => 's3',
],
注意
警告:
目前不能隊列 xls 導入。 PhpSpreadsheet 的 Xls 讀取器,包含了一些非 utf8 字符,導致無法隊列
12.行驗證
有時候我們想要在插入數據庫之前,驗證每一行。通過實現 WithValidation concern,我們可以指定每一行需要遵從的規則。
rule() 方法,希望返回一個 Laravel Validation 規則
<?php
namespace App\Imports;
use App\User;
use Maatwebsite\Excel\Concerns\ToModel;
use Maatwebsite\Excel\Concerns\Importable;
use Maatwebsite\Excel\Concerns\WithValidation;
class UsersImport implements ToModel, WithValidation
{
use Importable;
public function model(array $row)
{
return new User([
'name' => $row[0],
'email' => $row[1],
'password' => 'secret',
]);
}
public function rules(): array
{
return [
'1' => Rule::in(['[email protected]']),
// Above is alias for as it always validates in batches
'*.1' => Rule::in(['[email protected]']),
// Can also use callback validation rules
'0' => function($attribute, $value, $onFailure) {
if ($value !== 'Patrick Brouwers') {
$onFailure('Name is not Patrick Brouwers');
}
}
];
}
}
使用標題行進行驗證
當使用 WithHeadingRow concern,我們可以使用標題行名稱作爲規則屬性
<?php
namespace App\Imports;
use App\User;
use Maatwebsite\Excel\Concerns\ToModel;
use Maatwebsite\Excel\Concerns\Importable;
use Maatwebsite\Excel\Concerns\WithValidation;
use Maatwebsite\Excel\Concerns\WithHeadingRow;
class UsersImport implements ToModel, WithValidation, WithHeadingRow
{
use Importable;
public function model(array $row)
{
return new User([
'name' => $row['name'],
'email' => $row['email'],
'password' => 'secret',
]);
}
public function rules(): array
{
return [
'email' => Rule::in(['[email protected]']),
// Above is alias for as it always validates in batches
'*.email' => Rule::in(['[email protected]']),
];
}
}
自定義驗證消息
通過給導入添加 customValidationMessages() 方法,我們可以爲每個失敗的驗證指定自定義消息
/**
* @return array
*/
public function rules(): array
{
return [
'1' => Rule::in(['[email protected]']),
];
}
/**
* @return array
*/
public function customValidationMessages()
{
return [
'1.in' => 'Custom message for :attribute.',
];
}
自定義驗證屬性
通過給導入添加 customValidationAttributes() 方法,我們可以爲每一列指定自定義屬性名
/**
* @return array
*/
public function rules(): array
{
return [
'1' => Rule::in(['[email protected]']),
];
}
/**
* @return array
*/
public function customValidationAttributes()
{
return ['1' => 'email'];
}
處理驗證錯誤
數據庫事務
整個導入會自動包裝到一個數據庫事務中,這意味着每個錯誤將回滾整個導入。當使用批量插入時,只會回滾當前批次。
禁用事務
如果你更希望在導入(或塊導入)時,沒有任何數據庫事務,可以在配置中,改變想要的事務處理器:
在 config/excel.php
'transactions' => [
'handler' => 'db',
],
目前支持的處理器有:null 或 db
自定義事務處理器
如果想要自定義一個事務處理器(例如:MongoDB 數據庫),可以如下添加我們自己的處理器:
$this->app->make(\Maatwebsite\Excel\Transactions\TransactionManager::class)->extend('your_handler', function() {
return new YourTransactionHandler();
});
處理器應該實現 \Maatwebsite\Excel\Transactions\TransactionManager
在最後收集所有錯誤
當與批處理一起使用時,我們可以在導入結束後,收集所有的驗證失敗。我們可以 try-catch ValidationException。在此異常中,我們可以獲得所有失敗詳情。
每個失敗詳情是 Maatwebsite\Excel\Validators\Failure 的一個實例。Failure 包含的信息有:哪一行,哪一列,以及單元格的驗證錯誤
try {
$import->import('import-users.xlsx');
} catch (\Maatwebsite\Excel\Validators\ValidationException $e) {
$failures = $e->failures();
foreach ($failures as $failure) {
$failure->row(); // row that went wrong
$failure->attribute(); // either heading key (if using heading row concern) or column index
$failure->errors(); // Actual error messages from Laravel validator
$failure->values(); // The values of the row that has failed.
}
}
跳過失敗
有時候我們可能想要跳過失敗。通過使用 SkipsOnFailure concern,我們可以控制驗證失敗發生時的情況。使用 SkipsOnFailure,當發生失敗時,不會回滾整個導入
<?php
namespace App\Imports;
use App\User;
use Maatwebsite\Excel\Concerns\ToModel;
use Maatwebsite\Excel\Validators\Failure;
use Maatwebsite\Excel\Concerns\Importable;
use Maatwebsite\Excel\Concerns\SkipsOnFailure;
use Maatwebsite\Excel\Concerns\WithValidation;
class UsersImport implements ToModel, WithValidation, SkipsOnFailure
{
use Importable;
/**
* @param Failure[] $failures
*/
public function onFailure(Failure ...$failures)
{
// Handle the failures how you'd like.
}
}
如果想要自動跳過所有失敗的行,並在導入最後收集所有的失敗,可以使用 Maatwebsite\Excel\Concerns\SkipsFailures trait。
<?php
namespace App\Imports;
use App\User;
use Maatwebsite\Excel\Concerns\ToModel;
use Maatwebsite\Excel\Validators\Failure;
use Maatwebsite\Excel\Concerns\Importable;
use Maatwebsite\Excel\Concerns\SkipsOnFailure;
use Maatwebsite\Excel\Concerns\WithValidation;
use Maatwebsite\Excel\Concerns\SkipsFailures;
class UsersImport implements ToModel, WithValidation, SkipsOnFailure
{
use Importable, SkipsFailures;
}
已經跳過了,驗證規則失敗的每一行。現在我們可以在最後收集所有失敗:
$import = new UsersImport();
$import->import('users.xlsx');
foreach ($import->failures() as $failure) {
$failure->row(); // row that went wrong
$failure->attribute(); // either heading key (if using heading row concern) or column index
$failure->errors(); // Actual error messages from Laravel validator
$failure->values(); // The values of the row that has failed.
}
跳過錯誤
有時候我們可能想要跳過所有錯誤,例如:重複的數據庫記錄。通過使用 SkipOnError concern,我們可以控制模型導入失敗時的前那股看。使用 SkipOnError,當發生數據庫異常時,不會回滾整個導入
<?php
namespace App\Imports;
use App\User;
use Maatwebsite\Excel\Concerns\ToModel;
use Maatwebsite\Excel\Concerns\Importable;
use Maatwebsite\Excel\Concerns\SkipsOnError;
use Maatwebsite\Excel\Concerns\WithValidation;
class UsersImport implements ToModel, WithValidation, SkipsOnError
{
use Importable;
/**
* @param \Throwable $e
*/
public function onError(\Throwable $e)
{
// Handle the exception how you'd like.
}
}
如果想要自動跳過所有異常,並在導入最後收集所有它們,可以使用 Maatwebsite\Excel\Concerns\SkipsErrors trait。
<?php
namespace App\Imports;
use App\User;
use Maatwebsite\Excel\Concerns\ToModel;
use Maatwebsite\Excel\Validators\Failure;
use Maatwebsite\Excel\Concerns\Importable;
use Maatwebsite\Excel\Concerns\SkipsOnError;
use Maatwebsite\Excel\Concerns\WithValidation;
use Maatwebsite\Excel\Concerns\SkipsErrors;
class UsersImport implements ToModel, WithValidation, SkipsOnError
{
use Importable, SkipsErrors;
}
已經跳過了,發生錯誤的每一行。現在我們可以在最後收集所有錯誤:
$import = new UsersImport();
$import->import('users.xlsx');
dd($import->errors());
沒有 ToModel 的行驗證
如果沒有使用 ToModel concern,我們可以通過使用 Laravel Validator 非常簡單地進行行驗證。
<?php
namespace App\Imports;
use App\User;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Validator;
use Maatwebsite\Excel\Concerns\ToCollection;
class UsersImport implements ToCollection
{
public function collection(Collection $rows)
{
Validator::make($rows->toArray(), [
'*.0' => 'required',
])->validate();
foreach ($rows as $row) {
User::create([
'name' => $row[0],
]);
}
}
}
驗證規則
所有的驗證規則,參考 Laravel 驗證規則文檔
13.映射單元格
如果有一個更加自定義化的電子表格,並且只想訪問指定的單元格,可以實現 WithMappedCells concern
我們可能有如下的電子表格:
name Patrick Brouwers
email [email protected]
現在我們可以將 name 映射到 B1,將 email 映射到 B2。這些座標的值,之後將在給定的數組鍵下可用。
namespace App\Imports;
use App\User;
use Maatwebsite\Excel\Concerns\ToModel;
use Maatwebsite\Excel\Concerns\WithMappedCells;
new UsersImport implements WithMappedCells, ToModel
{
public function mapping(): array
{
return [
'name' => 'B1',
'email' => 'B2',
];
}
public function model(array $row)
{
return new User([
'name' => $row['name'],
'email' => $row['email'],
]);
}
}
警告:
該 concern 並不意味着映射列,只允許引用指定的單元格。
14.自定義 CSV 配置
默認情況下,Laravel Excel 使用配置文件(config/excel.php)的默認配置。我們可以通過添加 WithCustomCsvSettings 接口來改變該行爲。
namespace App\Imports;
use Maatwebsite\Excel\Concerns\ToModel;
use Maatwebsite\Excel\Concerns\WithCustomCsvSettings;
class UsersImport implements ToModel, WithCustomCsvSettings
{
public function model(array $row)
{
return new User([
'name' => $row['0'],
'email' => $row['1']
]);
}
public function getCsvSettings(): array
{
return [
'input_encoding' => 'ISO-8859-1'
];
}
}
可用的配置有:
delimiter
enclosure
line_ending
use_bom
include_separator_line
excel_compatibility
escape_character
contiguous
input_encoding
15.進度條
當通過 console 來導入一個 Excel 文件,我們可以實現 WithProgressBar concern 來顯示一個進度條。
例如,我們的導入類如下所示:
<?php
namespace App\Imports;
use App\User;
use Illuminate\Support\Facades\Hash;
use Maatwebsite\Excel\Concerns\Importable;
use Maatwebsite\Excel\Concerns\ToModel;
use Maatwebsite\Excel\Concerns\WithProgressBar;
class UsersImport implements ToModel, WithProgressBar
{
use Importable;
public function model(array $row)
{
return new User([
'name' => $row[0],
'email' => $row[1],
'password' => Hash::make($row[2]),
]);
}
}
在我們的 console 命令中,如下使用:
<?php
namespace App\Console\Commands;
use App\Imports\UsersImport;
use Illuminate\Console\Command;
class ImportExcel extends Command
{
protected $signature = 'import:excel';
protected $description = 'Laravel Excel importer';
public function handle()
{
$this->output->title('Starting import');
(new UsersImport)->withOutput($this->output)->import('users.xlsx');
$this->output->success('Import successful');
}
}
在命令行,通過調用 php artisan import:excel,開始導入。現在應該就會看到開始信息,進度條,以及完成後的成功信息
16.導入 concern
看文檔
17.擴展
看文檔
18.測試
Excel facade 可用於將導入器變換爲 fake,來進行測試??
看文檔