项目里需要导入了,正好 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 dong@123.com 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(['patrick@maatwebsite.nl']),
// Above is alias for as it always validates in batches
'*.1' => Rule::in(['patrick@maatwebsite.nl']),
// 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(['patrick@maatwebsite.nl']),
// Above is alias for as it always validates in batches
'*.email' => Rule::in(['patrick@maatwebsite.nl']),
];
}
}
自定义验证消息
通过给导入添加 customValidationMessages() 方法,我们可以为每个失败的验证指定自定义消息
/**
* @return array
*/
public function rules(): array
{
return [
'1' => Rule::in(['patrick@maatwebsite.nl']),
];
}
/**
* @return array
*/
public function customValidationMessages()
{
return [
'1.in' => 'Custom message for :attribute.',
];
}
自定义验证属性
通过给导入添加 customValidationAttributes() 方法,我们可以为每一列指定自定义属性名
/**
* @return array
*/
public function rules(): array
{
return [
'1' => Rule::in(['patrick@maatwebsite.nl']),
];
}
/**
* @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 patrick@xxx.nl
现在我们可以将 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,来进行测试??
看文档