laravel-excel maatwebsite excel 导入的中文文档

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

	看文档

 

 类似资料: