kczer / excel-importer-bundle
针对 Symfony 框架调整的 kczer/excel-importer 库
Requires
- php: >=8.0
- ext-ctype: *
- ext-json: *
- doctrine/annotations: ~1.0
- phpoffice/phpspreadsheet: ^1.3
- symfony/config: >=3.3
- symfony/console: >=4.4.0
- symfony/dependency-injection: >=3.3
- symfony/http-kernel: >=3.3
- symfony/translation: >=4.0
- symfony/twig-bundle: >=4.4.0
- symfony/yaml: >=4.0
Requires (Dev)
- symfony/framework-bundle: ^4.3
- symfony/test-pack: ^1.0
README
目录
ExcelImporterBundle
ExcelImportedBundle 允许从 EXCEL 文件导入 PHP 对象,以及导出相同的对象。
安装
您可以使用 composer 如此安装它
composer require kczer/excel-importer-bundle
文档
示例导入模型- @ExcelColumn 注解
首先,我们需要创建一个模型来表示 EXCEL 文件内容。在这里,我们使用 @ExcelColumn 注解,以下是一些选项
-
columnKey(支持翻译键):可以是技术 EXCEL 列名(A-ZZZ... 表示法)或 命名列名(推荐),这将作为 EXCEL 文件中的值。为了使命名列名正常工作,我们需要有一行与所有列名匹配。命名和技术列键不得混合。
-
cellName(支持翻译键):用于显示错误消息的名称,也可以用于从 ExcelRow 实例直接显示。
-
targetExcelCellClass:将用于解析 excel 值到属性的 Excel 单元格类。一些内置的类可供选择。
-
required:值是否为 EXCEL 文件中的必需值(默认为 true)
-
options:针对 EXCEL 单元格的附加选项。支持以下选项
示例导入 - 模型(技术键)
EXCEL 文件的示例模型可能如下所示
<?php //TestModel.php use Kczer\ExcelImporterBundle\Annotation\ExcelColumn; use Kczer\ExcelImporterBundle\ExcelElement\ExcelCell\StringExcelCell; use Kczer\ExcelImporterBundle\ExcelElement\ExcelCell\IntegerExcelCell; class TestModel { /** * @ExcelColumn(columnKey="A", cellName="id", targetExcelCellClass=IntegerExcelCell::class) * * @var int */ private $id; /** * @ExcelColumn(columnKey="B", cellName="name", targetExcelCellClass=StringExcelCell::class, required=false) * * @var string|null */ private $name; // Model class MUST have public getters and setters for mapped properties // to let importer prepare the model public function getId(): int { return $this->id; } public function setId(int $id): self { $this->id = $id; return $this; } public function getName(): ?string { return $this->name; } public function setName(?string $name): self { $this->name = $name; return $this; } }
以下 EXCEL 文件将匹配此模型
示例导入 - 模型(命名列键)
使用命名列键的上述相同模型可能如下所示
<?php //TestModel.php use Kczer\ExcelImporterBundle\Annotation\ExcelColumn; use Kczer\ExcelImporterBundle\ExcelElement\ExcelCell\StringExcelCell; use Kczer\ExcelImporterBundle\ExcelElement\ExcelCell\IntegerExcelCell; class TestModel { // column key can be also a translation key /** * @ExcelColumn(columnKey="id", cellName="id", targetExcelCellClass=IntegerExcelCell::class) * * @var int */ private $id; /** * @ExcelColumn(columnKey="name", cellName="name", targetExcelCellClass=StringExcelCell::class, required=false) * * @var string|null */ private $name; // Model class MUST have public getters and setters for mapped properties // to let importer prepare the model public function getId(): int { return $this->id; } public function setId(int $id): self { $this->id = $id; return $this; } public function getName(): ?string { return $this->name; } public function setName(?string $name): self { $this->name = $name; return $this; } }
示例导入 - 显示模型
有时我们希望显示一些导入的内容或查看导入器生成的验证消息。可以通过创建扩展 AbstractDisplayModel 类的显示模型类来实现。显示模型类必须具有与模型相同的属性名称(对于所有映射字段),并且必须将它们声明为字符串。上述模型的显示模型类可能如下所示
<?php //TestModel.php use Kczer\ExcelImporterBundle\Annotation\ExcelColumn; use Kczer\ExcelImporterBundle\ExcelElement\ExcelCell\StringExcelCell; use Kczer\ExcelImporterBundle\ExcelElement\ExcelCell\IntegerExcelCell; use \Kczer\ExcelImporterBundle\Model\AbstractDisplayModel; class TestDisplayModel extends AbstractDisplayModel { /** @var string */ private $id; /** @var string */ private $name; public function getId(): string { return $this->id; } public function setId(string $id): self { $this->id = $id; return $this; } public function getName(): string { return $this->name; } public function setName(string $name): self { $this->name = $name; return $this; } }
通过扩展 AbstractDisplayModel 类,我们有两个可访问的方法
- isValid:根据模型的有效性返回 true 或 false
- getMergedAllErrorMessages:返回包含所有属性错误消息的字符串
示例导入
通过导入 ModelExcelImporterFactory 并创建 ModelExcelImporter 实例来导入上面定义的模型
<?php //TestService.php use \Kczer\ExcelImporterBundle\Importer\Factory\ModelExcelImporterFactory; class TestService { /** @var ModelExcelImporterFactory */ private $modelExcelImporterFactory; public function __construct(ModelExcelImporterFactory $modelExcelImporterFactory) { $this->modelExcelImporterFactory = $modelExcelImporterFactory } /** * @return TestModel[] */ public function getImportedModels(string $excelFilePath): array { $importer = $this->modelExcelImporterFactory->createModelExcelImporter(TestModel::class, TestDisplayModel::class); //Display model class can be null if not needed $importer->parseExcelFile($excelFilePath); return $importer->getModels(); //$importer->getDisplayModels() for display models } }
parseExcelFile 方法接受两个参数
- excelFilePath:EXCEL 文件的完整路径
- firstRowMode(可选,仅适用于技术列键):告诉导入器如何处理第一行数据。可用选项
- AbstractExcelImporter::FIRST_ROW_MODE_SKIP(默认):跳过第一行数据
- AbstractExcelImporter::FIRST_ROW_MODE_DONT_SKIP:不跳过第一行数据
- AbstractExcelImporter::FIRST_ROW_MODE_SKIP_IF_INVALID:仅在第一行数据无效时跳过第一行数据
示例导入 - 复杂验证
有时需要进行更复杂的验证,例如列之间的验证,当一个单元格的值依赖于其他单元格时。例如,我们希望每个名称单元格以相应的 ID 结尾。这可以通过调用 setRowRequirementsValidator 来实现
<?php //TestService.php use \Kczer\ExcelImporterBundle\Importer\Factory\ModelExcelImporterFactory; use \Kczer\ExcelImporterBundle\ExcelElement\ExcelRow; use function substr; use function strlen; class TestService { /** @var ModelExcelImporterFactory */ private $modelExcelImporterFactory; public function __construct(ModelExcelImporterFactory $modelExcelImporterFactory) { $this->modelExcelImporterFactory = $modelExcelImporterFactory } /** * @return TestModel[] */ public function getImportedModels(string $excelFilePath): array { $importer = $this->modelExcelImporterFactory->createModelExcelImporter(TestModel::class, TestDisplayModel::class); //Display model class can be null if not needed $importer->setRowRequirementsValidator([$this, 'validateRowRequirements']);//Or with standard anonymous function syntax $importer->parseExcelFile($excelFilePath); return $importer->getModels(); } public function validateRowRequirements(ExcelRow $excelRow): void { $excelCells = $excelRow->getExcelCells(); // Array of ExcelCell objects, keys are column keys (technical or named: named in example) $idString = (string)$excelCells['id']->getValue(); // Already parsed value; $excelCells['id']->getRawValue() for string|null if ($idString !== substr($excelCells['name']->getValue(), -1 * strlen($idString))) { $excelRow->addErrorMessage('Name should end with the corresponding id'); // Supports translation keys } } }
示例导入-有用的方法
以下是导入器的一些有用方法
<?php //TestService.php use \Kczer\ExcelImporterBundle\Importer\Factory\ModelExcelImporterFactory; use \Kczer\ExcelImporterBundle\Importer\ModelExcelImporter; class TestService { /** @var ModelExcelImporter */ private $modelExcelImporter; //. //. //. public function foo(): void { $this->modelExcelImporter->getExcelRowsAsJson(); // Get imported as JSON $this->modelExcelImporter->parseJson(); // Import JSON back to models $this->modelExcelImporter->hasErrors(); // Are there any validation errors in EXCEL file? } }
示例导入-注解验证
捆绑包包含两个内置验证,可以从模型级别使用
- @Validator\Regex:验证Excel单元格值是否符合正则表达式(正则表达式不区分大小写)
- @Validator\Length:验证Excel单元格长度
每个验证器都有一个message属性,指定验证失败时的错误消息。这些消息支持翻译,每个验证器传递特定的翻译参数。支持的翻译参数
- @Validator\Regex:
- %pattern%:在注解中传递的模式
- @Validator\Length:
- %minLength%:在注解中传递的最小长度
- %maxLength%:在注解中传递的最大长度
如果我们想使名称字段始终以id结尾,并且最长为10个字符,我们可以键入
<?php //TestModel.php use Kczer\ExcelImporterBundle\Annotation\ExcelColumn; use Kczer\ExcelImporterBundle\ExcelElement\ExcelCell\StringExcelCell; use Kczer\ExcelImporterBundle\ExcelElement\ExcelCell\IntegerExcelCell; use Kczer\ExcelImporterBundle\Annotation\Validator; class TestModel { // . // . // . /** * @ExcelColumn(columnKey="name", cellName="name", targetExcelCellClass=StringExcelCell::class, required=false) * @Validator\Regex(pattern="[a-z]+\d+", message="Name must end with the id") In case of error adds message "Name must end with the id" to cell errors * @Validator\Length(maxLength=10) In case of error adds message "Value's max length is 10" to cell errors * * @var string|null */ private $name; // . // . // . }
<?php //TestModel.php use Kczer\ExcelImporterBundle\Annotation\ExcelColumn; use Kczer\ExcelImporterBundle\ExcelElement\ExcelCell\StringExcelCell; use Kczer\ExcelImporterBundle\ExcelElement\ExcelCell\IntegerExcelCell; class TestModel { // column key can be also a translation key /** * @ExcelColumn(columnKey="id", cellName="id", targetExcelCellClass=IntegerExcelCell::class) * * @var int */ private $id; /** * @ExcelColumn(columnKey="name", cellName="name", targetExcelCellClass=StringExcelCell::class, required=false) * * @var string|null */ private $name; // Model class MUST have public getters and setters for mapped properties // to let importer prepare the model public function getId(): int { return $this->id; } public function setId(int $id): self { $this->id = $id; return $this; } public function getName(): ?string { return $this->name; } public function setName(?string $name): self { $this->name = $name; return $this; } }
示例导出
有了我们的模型,我们不仅可以从EXCEL文件中导入它,还可以导出它并将其合并到现有的EXCEL文件中。
<?php //TestExportService.php use Kczer\ExcelImporterBundle\Exporter\ModelExcelExporter; class TestExportService { /** @var ModelExcelExporter */ private $modelExcelExporter; //. //. //. /** * @param TestModel[] $models */ public function exportModels(array $testModels, string $existingExcelFilePath): void { $newExcelFileTmpPath = $this->modelExcelExporter->exportModelsToNewFile($testModels); //Returns path to newly created TMP file $newMergedExcelFileTmpPath = $this->modelExcelExporter->exportAndMergeModelsToExistingFile($testModels, $existingExcelFilePath) } }
有两种导出方法
- exportModelsToNewFile:创建新的EXCEL文件并将模型输出到其中。该方法最多可接受三个参数
- models:要导出的模型
- newFileNameWithoutExtension:生成的无扩展名EXCEL文件名。如果提供null,则为随机名称
- outputHeaders:是否添加标题列
- exportAndMergeModelsToExistingFile:创建新的EXCEL文件并将模型输出到其中。该方法最多可接受三个参数
- models:与上面相同
- newFileNameWithoutExtension:与上面相同
- comparer:可以是用于比较的模型属性名称或匿名函数,该函数接受两个模型作为参数,并返回一个布尔值,表示两个模型是否相等。如果两个模型相等,则EXCEL中的一个将被导入的模型替换
- firstRowMode:将firstRowMode传递给导入器以从EXCEL获取数据时的第一行模式(仅适用于技术列键)
AbstractDictionaryExcelCell
字典EXCEL单元格用于定义单元格可以取的“范围”值。当单元格值必须匹配数据库中的某些列值时使用它。示例DictionaryClass类
<?php class SampleDictionaryExcelCell extends AbstractDictionaryExcelCell { /** @var TestRepository */ private $testRepository; public function __construct(TestRepository $testRepository) { $this->myRepository = $testRepository; } /** * @inheritDoc */ protected function getDictionary(): array { return $this->testRepository->findIndexedBySomeUniqeCode(); // array in format ['some code expected in excel' => $valueInExcel, ...] } }
Yaml 配置
以下示例显示了可以在config/packages/kczer_excel_importer.yaml文件中放置的所有可用配置选项
kczer_excel_importer: excel_cell: bool: # BoolExcelCell Configuration empty_as_false: false # Treat empty values as false? (default to true) true_values: # Values that override default true values - absolutely - of course - ofc false_values: # Values that override default false values - absolutely not - of course not - ofcn