kczer / excel-importer
用于可配置EXCEL数据导入的库
Requires
- php: >=7.1.3
- ext-ctype: *
- ext-json: *
- doctrine/annotations: ~1.0
- phpoffice/phpspreadsheet: ^1.3
This package is auto-updated.
Last update: 2024-09-25 13:58:52 UTC
README
目录
Excel-importer
Excel-importer 是一个PHP库,它允许轻松导入EXCEL格式的数据并将其解析为对象。
安装
您可以使用composer安装它,如下所示
composer require kcze/excel-importer
文档
创建简单的EXCEL数据导入
首先,您需要创建一个扩展 Kczer\ExcelImporter\AbstractExcelImporter 的 Import 类。
对于版本 >= 3.0
<?php namespace My\SampleNamespace; use Kczer\ExcelImporter\AbstractExcelImporter; use Kczer\ExcelImporter\ExcelElement\ExcelCell\Configuration\ExcelCellConfiguration; use Kczer\ExcelImporter\ExcelElement\ExcelCell\IntegerExcelCell; use Kczer\ExcelImporter\ExcelElement\ExcelCell\StringExcelCell; use Kczer\ExcelImporter\Exception\ExcelCellConfiguration\UnexpectedExcelCellClassException; class MySimpleExcelImporter extends AbstractExcelImporter { protected function configureExcelCells(): void { $this ->addExcelCell(StringExcelCell::class, 'Cell header name', 'A', false) ->addExcelCell(IntegerExcelCell::class, 'Another header name', 'B'); } public function processParsedData(): void { // Since 3.1, you can pass callback as first argument to this function to modify the message // Separator is now the second parameter $excelRow->getMergedErrorMessage(static function(string $message): string { //Do something with the message }); // The rest is same as before 3.0 } }
对于版本 < 3.0
<?php namespace My\SampleNamespace; use Kczer\ExcelImporter\AbstractExcelImporter; use Kczer\ExcelImporter\ExcelElement\ExcelCell\Configuration\ExcelCellConfiguration; use Kczer\ExcelImporter\ExcelElement\ExcelCell\IntegerExcelCell; use Kczer\ExcelImporter\ExcelElement\ExcelCell\StringExcelCell; use Kczer\ExcelImporter\Exception\ExcelCellConfiguration\UnexpectedExcelCellClassException; class MySimpleExcelImporter extends AbstractExcelImporter { /** * @return ExcelCellConfiguration[] */ protected function getExcelCellConfigurations(): array { // This method returns row configuration for Your import. // Keys are column keys (can be int indexed but it's not reccommended). /* Values are configurations that needs to be pased AbstractExcelClass extending class names, column names, and (optinoally) tell if cell is required (with default to true). */ return [ 'A' => new ExcelCellConfiguration(StringExcelCell::class, 'Cell header name', false), 'B' => new ExcelCellConfiguration(IntegerExcelCell::class, 'Another header name'), ]; } public function processParsedData(): void { $this->hasErrors(); // Check if any validation error occurred // We can do something with Data. // Gets an array of ExcelRow objects $excelRows = $this->getExcelRows(); foreach ($excelRows as $excelRow) { // The same as above for one row $excelRow->hasErrors(); // Gets merged messages from all cells for current row by separator given in argument (default ' | ') $excelRow->getMergedErrorMessage(); // Gets an array of ExcelCells (the same as configured in getExcelCellConfigurations()) $excelCells = $excelRow->getExcelCells(); // Gets value of current row 'A' cell $excelCells['A']->getValue(); // Gets int-parsed value of current row 'B' cell $excelCells['B']->getValue(); // Gets string representation of value ready to dispaly (the same value as given in EXCEL file for predefined ExcelCells) $excelCells['B']->getDisplayValue(); } } }
⚠️ 自版本 3.0 起不再支持 int 列键。
然后使用服务
<?php use My\SampleNamespace\MySimpleExcelImporter; $importer = new MySimpleExcelImporter(); // Second parameter tells whether to omit first row, or not (with default to true) $importer->parseExcelData('some/file/path/excelFile.xlsx', true); $importer->processParsedData();
⚠️ 自版本 3.0 起导入始终忽略第一行。
在 processParsedData() 中显示的所有方法都是公共的,因此可以从服务外部访问。如您在配置中看到的那样,使用了 StringExcelCell 和 IntegerExcelCell 类。这些类用于对EXCEL单元格值执行适当的解析和验证。您可以创建自己的 ExcelCell 类 - 更多信息见下文。
预定义的 ExcelCell 类
- StringExcelCell - 简单的字符串值,不验证数据。getValue() 返回字符串。
- IntegerExcelCell - 仅接受有效的整数。getValue() 返回整数。
- FloatExcelCell - 仅接受有效的数字。getValue() 返回浮点数。
- BoolExcelCell - 接受 'y', 'yes', 't', 'tak', 't', 'true'(不区分大小写)作为真。其他值视为假。getValue() 返回布尔值。
- DateTimeExcelCell - 接受 DateTime 类构造函数接受的任何字符串。getValue() 返回 DateTime 对象。
- AbstractDictionaryExcelCell - 适用于键值类型示例的抽象类。
- AbstractMultipleDictionaryExcelCell - 可以将多个 AbstractDictionaryCell 字典合并为一个的抽象类。
恭喜!您已创建了自己的第一个简单的EXCEL导入类。虽然如此,使用 Excel-importer 进行导入还有更好的更快的方法。
将EXCEL数据映射到对象
在上一个例子中,导入的结果是 ExcelRow 对象数组,但如果我们想将EXCEL数据映射到某个模型对象呢?假设我们有一个名为 SomeModel 的PHP对象。我们可以通过扩展 Kczer\ExcelImporter\AbstractModelExcelImporter 来导入它。
<?php namespace My\SampleNamespace; use Kczer\ExcelImporter\AbstractModelExcelImporter; class SomeModelImporter extends AbstractModelExcelImporter { /** * @inheritDoc */ public function processParsedData(): void { // Gets array of SomeModel objects // Note that models are created ONLY if $this->hasErrors() return false $this->getModels(); } /** * @inheritDoc */ protected function getImportModelClass(): string { return SomeModel::class;// Our model class } }
它几乎准备好了。要使其工作,我们需要进行一个额外的设置步骤。在我们的 SomeModel 类中
<?php namespace My\SampleNamespace; use DateTime; use Kczer\ExcelImporter\Annotation\ExcelColumn; use Kczer\ExcelImporter\ExcelElement\ExcelCell\StringExcelCell; use Kczer\ExcelImporter\ExcelElement\ExcelCell\IntegerExcelCell; use Kczer\ExcelImporter\ExcelElement\ExcelCell\DateTimeExcelCell; class SomeModel { /** * @ExcelColumn(cellName="Name", targetExcelCellClass=StringExcelCell::class, columnKey="A") * * @var string */ private $name; /** * @ExcelColumn(cellName="Code", targetExcelCellClass=IntegerExcelCell::class, columnKey="B") * * @var int */ private $code; /** * @ExcelColumn(cellName="Some date", targetExcelCellClass=DateTimeExcelCell::class, columnKey="C", required=false) * * @var ?DateTime */ private $someDate; public function getName(): string { return $this->name; } public function setName(string $name): void { $this->name = $name; } public function getCode(): int { return $this->code; } public function setCode(int $code): void { $this->code = $code; } public function getSomeDate(): ?DateTime { return $this->someDate; } public function setSomeDate(?DateTime $someDate): void { $this->someDate = $someDate; } }
以下代码中最重要的部分是 ExcelColumn 注解类,它告诉导入器有关我们的模型数据类型、名称、列键(再次,键列可以省略,然后采用属性顺序,但不会被考虑)和单元格必填的信息。这两个类就是您需要做的全部。Excel导入器会做其余的工作,并为您创建模型实例。
DictionaryExcelCell
字典EXCEL单元格用于定义单元格可以有的“范围”值。当单元格值可以是例如数据库中某个资源的ID时,它非常完美。示例 DictionaryExcelCell 类
<?php namespace My\SampleNamespace; use Kczer\ExcelImporter\ExcelElement\ExcelCell\AbstractDictionaryExcelCell; class SampleDictionaryExcelCell extends AbstractDictionaryExcelCell { /** * @inheritDoc */ protected function getDictionary(): array { return [ 1 => new User('user 1'), 2 => new User('user 2'), 3 => new User('user 3'), 4 => new User('user 4'), ]; } }
现在,我们只需将此类添加到导入配置中,或将它添加到 ExcelColumn 注解中,Excel-importer 将只接受范围 1-4 的值,并且 getValue 将返回 User 对象。
MultipleDictionaryExcelCell
假设某一列可以包含来自一个字典或另一个字典的值。MultipleDictionaryExcelCell 是这种情况下的完美工具。
<?php namespace My\SampleNamespace; use Kczer\ExcelImporter\ExcelElement\ExcelCell\AbstractMultipleDictionaryExcelCell; class SampleMultidictionaryExcelCell extends AbstractMultipleDictionaryExcelCell { /** * @inheritDoc */ public function getSubDictionaryExcelCellClasses(): array { return [ SomeDictionaryClass::class, SomeOtherDictionaryClasss::class ]; } }
现在,Excel-import 接受来自 SomeDictionaryClass 和 SomeOtherDictionaryClasss 字典的值(如果键相交,则具有较低数组键的类具有优先权)。
自定义ExcelCellClasses
如果您希望ExcelCell类更加灵活或需要进行更多验证,您只需扩展AbstractExcelCellClass并创建自定义验证和返回数据类型。在示例中,我们将创建需要是有效电子邮件的单元格。
<?php namespace My\SampleNamespace; use Kczer\ExcelImporter\ExcelElement\ExcelCell\AbstractExcelCell; class RegexValidatableExcelCell extends AbstractExcelCell { /** * returned value will be returned by the getValue() method * Note, that getValue() will return this value only if cell doesn't contain any error */ protected function getParsedValue(): ?string { // In this case, we don't want to do any parsing as string is proper data type for email address return $this->rawValue; } /** * Method should return null if value is valid, * or string with error message if not */ protected function validateValueRequirements(): ?string { // We can access the raw string value with $this->rawValue // Note that the raw value will be null in case of empty cell if (filter_var($this->rawValue, FILTER_VALIDATE_EMAIL) === false) { // Below method creates error message in format [cellName] - [given_message] return $this->createErrorMessageWithNamePrefix('Value is not a valid email address'); } return null; } }
更复杂的导入
有时,我们需要验证行内单元格之间的依赖关系,甚至行之间的依赖关系。我们也可以这样做。AbstractExcelImporter实现了可被覆盖的checkRow checkRowRequirements()
方法,可以用来检查所需的依赖关系并在需要时添加一些错误。该方法在AbstractModelExcelImporter
创建模型之前被调用,因此我们仍然能够从EXCEL数据中创建对象。
依赖关系验证示例
假设我们有一个模型
<?php namespace My\SampleNamespace; use Kczer\ExcelImporter\Annotation\ExcelColumn; use Kczer\ExcelImporter\ExcelElement\ExcelCell\IntegerExcelCell; class SampleModelClass { /** * @ExcelColumn(cellName="Number 1", targetExcelCellClass=IntegerExcelCell::class, columnKey="A") * * @var int */ private $num1; /** * @ExcelColumn(cellName="Number 1", targetExcelCellClass=IntegerExcelCell::class, columnKey="B") * * @var int */ private $num2; public function getNum1(): int { return $this->num1; } public function setNum1(int $num1): void { $this->num1 = $num1; } public function getNum2(): int { return $this->num2; } public function setNum2(int $num2): void { $this->num2 = $num2; } }
假设num1应该大于num2。我们可以这样验证这个依赖关系
<?php namespace My\SampleNamespace; use Kczer\ExcelImporter\AbstractModelExcelImporter; class DependencyValidationExcelImport extends AbstractModelExcelImporter { protected function checkRowRequirements(): void { foreach ($this->getExcelRows() as $excelRow) { $exclCells = $excelRow->getExcelCells(); if ($exclCells['A']->getValue() <= $exclCells['B']->getValue()) { $excelRow->addErrorMessage('Number 1 should be bigger than Number 2'); } } } /** * @inheritDoc */ public function processParsedData(): void { // TODO: Implement processParsedData() method. } /** * @inheritDoc */ protected function getImportModelClass(): string { return SampleModelClass::class; } }
如果验证添加了任何错误,那么EXCEL将被视为无效,因此模型将不会被创建。
数据编码
如果您想对导入器中的数据进行编码(例如,与请求一起发送),可以这样做
<?php $serializedInstance = $importer->serializeInstance();
... 然后重新创建导入器实例
<?php MyImporterClass::createFromSerialized($serializedInstance);
从版本3.0开始,也可以将行作为JSON获取
<?php $rowsJson = $importer->getExcelRowsAsJson();
... 然后从该JSON重新创建行
<?php $importer->parseJson($rowsJson);