2020-04-22

项目里需要导入了,正好 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,来进行测试??

看文档

推荐阅读 更多精彩内容

  • pyspark.sql模块 模块上下文 Spark SQL和DataFrames的重要类: pyspark.sql...
    mpro 阅读 9,146 评论 0 13
  • 我前几天在群上分享了一个视频,是一个汽车博主拍的视频,带大家去北京银泰购物中心,一个地方看遍有钱人的衣食住行,拥有...
    cn677 阅读 292 评论 0 1
  • 第一步: git config --global user.name "Your Name" :配置用户名;git...
    乔乔乔0126 阅读 210 评论 0 0
  • 文/暖风浅浅 踏青的季节似乎来得晚了些,当我怀着一颗虔诚心来到湖水边,湖边的柳树正悄然无声地变绿,那种绿色远望是一...
    墨读时光 阅读 441 评论 1 18
  • 早晨7:15赶到阳江一中听刘老师的第一节课。大家兴致勃勃毫无怨言。今天上《范进中举》。课前演讲、读写同步,老师们专...
    杜老师爱咖啡 阅读 202 评论 0 0