kczer/excel-importer-bundle

针对 Symfony 框架调整的 kczer/excel-importer 库

5.8 2022-10-21 08:40 UTC

This package is auto-updated.

Last update: 2024-09-21 13:04:03 UTC


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 文件将匹配此模型

technical_column_keys_simple_model_excel_content.png

示例导入 - 模型(命名列键)

使用命名列键的上述相同模型可能如下所示

<?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