mathielen / import-engine

完整的导入栈,可导入几乎任何数据到您的应用程序中

0.8.6 2015-12-17 10:34 UTC

This package is auto-updated.

Last update: 2024-09-08 21:33:01 UTC


README

Build Status Scrutinizer Code Quality Code Coverage SensioLabsInsight Latest Stable Version

完整的导入栈,可导入几乎任何数据到您的应用程序中。也可以用于导出。

简介

该库基于优秀的Ddeboer Data Import库实现了一些高级功能。由于Data-Import库提供了一个很好的工具包,可以快速、干净地实现数据导入/导出过程,因此还需要做很多工作才能为您的应用程序设置一个完整的导入器。这个库可以帮助您完成这项工作。

如果您正在开发一个Symfony2项目,您可能想使用ImportEngineBundle的舒适配置。

特性

  • 支持自动分隔符发现等功能,如自动分隔符发现或处理压缩文件。目前支持以下存储
    • 结构化文件
      • CSV、XML、JSON、Excel
      • 文件可能已压缩
    • Doctrine2查询
    • 服务端点
  • 存储配置。为导入提供可能的存储容器列表。例如,本地文件、远程文件、上传的文件、数据库连接、服务端点等。
  • 映射子系统,用于构建各种导入映射:字段-字段、字段-转换器-字段、字段-转换器-对象等。
  • 使用JMSSerializer自动映射到对象树
  • 使用Symfony Validation进行源(读取)和目标(写入)验证。可以使用注解。
  • 使用Symfony EventDispatcher集成事件系统
  • 几乎保留Ddeboer Data Import库提供的所有灵活性。
  • 代码经过良好测试。

安装

该库可在Packagist上获取。推荐通过Composer安装。

$ composer require mathielen/import-engine

然后包含Composer的自动加载器

require_once 'vendor/autoload.php';

如果您想使用Excel文件,请确保在项目中包含phpoffice/phpexcel

$ composer require phpoffice/phpexcel

快速入门

将任意文件导入到您的系统中

使用*Provider功能,您可以让导入系统确定文件的格式以及应使用哪些抽象类。

$service = new TestEntities\Dummy(); //your domain service

$fileStorageProvider = new Mathielen\ImportEngine\Storage\Provider\FileStorageProvider();
$storageSelection = $fileStorageProvider->select('tests/metadata/testfiles/flatdata.csv');
$sourceStorage = $fileStorageProvider->storage($storageSelection);

$targetStorage = new Mathielen\ImportEngine\Storage\ServiceStorage(array($service, 'onNewData'));
$importer = Mathielen\ImportEngine\Importer\Importer::build($targetStorage);

$import = Mathielen\ImportEngine\Import\Import::build($importer, $sourceStorage);

$importRunner = new Mathielen\ImportEngine\Import\Run\ImportRunner();
$importRunner->run($import);

更多示例

请参阅:https://github.com/mathielen/import-engine/tree/master/tests/functional/Mathielen/ImportEngine

用法

术语

  • 一个导入器是整个导入过程的基定义。它说明了可以导入什么以及导入到何处。它由以下部分组成
    • (可选)一个StorageProvider,代表选择源存储的“虚拟文件系统”
    • (可选)一个SourceStorage,它可能是一个文件、一个数据库表、一个数组、一个对象树等
    • 一个TargetStorage,它可能是一个文件、一个数据库表、一个数组、一个对象树等
    • 一个映射,可能包含转换器、字段映射等。
    • 一个验证,可能包含从源存储读取的数据的验证规则以及将要写入目标存储的数据的验证规则。
    • 一个事件系统,用于实现过程中的详细日志记录或其他交互。
  • 导入是对导入过程的特定定义。它使用导入器,并包含处理数据所必需的所有特定信息。这包括一个特定的源存储和一个映射
  • 导入运行器用于处理导入。
  • 每次导入的运行都由一个导入运行表示。

存储提供者

存储提供者代表一个“虚拟文件系统”,用于选择可以用于导入源或目标的源存储

FinderFileStorageProvider

使用Symfony Finder Component作为可能导入的文件集合。

use Symfony\Component\Finder\Finder;
use Mathielen\ImportEngine\Storage\Provider\FinderFileStorageProvider;

$finder = Finder::create()
  ->in('tests/metadata/testfiles')
  ->name('*.csv')
  ->name('*.tab')
  ->size('>0K')
;
  
$ffsp = new FinderFileStorageProvider($finder);

DoctrineQueryStorageProvider

您可以使用特定的Doctrine查询或仅实体类名(查询将为SELECT * FROM)作为可能的源存储。

use Symfony\Component\Finder\Finder;
use Mathielen\ImportEngine\Storage\Provider\DoctrineQueryStorageProvider;

$em = ... //Doctrine2 EntityManager
$qb = $em->createQueryBuilder()
  ->select('a')
  ->from('MySystem\Entity\Address', 'a')
  ->andWhere('a.id > 10')
;

$queries = array(
  'MySystem/Entity/MyEntity',
  $qb
);

$desp = new DoctrineQueryStorageProvider($em, $queries);

UploadFileStorageProvider

您可以使用提供者来简化文件上传。

use Mathielen\ImportEngine\Storage\Provider\UploadFileStorageProvider;

$ufsp = new UploadFileStorageProvider('/tmp'); //path to where the uploaded files will be transferred to

文件存储提供者的自动CSV分隔符发现

文件存储提供者可能使用存储工厂来构建存储对象。默认情况下使用FormatDiscoverLocalFileStorageFactory。此存储工厂使用MimeTypeDiscoverStrategy来确定所选文件的MIME类型,并使用它来创建正确的存储处理程序。您可以更改此行为或扩展它。有一个CsvAutoDelimiterTypeFactory,您可以使用它来自动猜测CSV文件的正确分隔符。

use Mathielen\ImportEngine\Storage\Format\Factory\CsvAutoDelimiterFormatFactory;
use Mathielen\ImportEngine\Storage\Factory\FormatDiscoverLocalFileStorageFactory;
use Mathielen\ImportEngine\Storage\Format\Discovery\MimeTypeDiscoverStrategy;

$ffsp = ...
$ffsp->setStorageFactory(
  new FormatDiscoverLocalFileStorageFactory(
    new MimeTypeDiscoverStrategy(array(
      'text/plain' => new CsvAutoDelimiterFormatFactory()
))));

这样,任何具有text/plain MIME类型的文件都将传递给CsvAutoDelimiterFormatFactory以确定分隔符。

存储

存储是数据的容器。存储为其本身提供读取器和写入器实现。

use Mathielen\ImportEngine\Storage\ArrayStorage;
use Mathielen\ImportEngine\Storage\DoctrineStorage;
use Mathielen\ImportEngine\Storage\LocalFileStorage;
use Mathielen\ImportEngine\Storage\Format\CsvFormat;

$em = ... //Doctrine2 EntityManager

$array = array(1,2,3);
$storage = new ArrayStorage($array);
$storage = new DoctrineStorage($em, 'MyEntities\Entity');
$storage = new LocalFileStorage('tests/metadata/testfiles/flatdata.csv', new CsvFormat());
$storage = new ServiceStorage(array($service, 'myMethod')); //callable

验证

您可以使用以下方法获取源和目标验证错误:

$import = ...
$import->importer()->validation()->getViolations();

源数据验证

use Mathielen\ImportEngine\Validation\ValidatorValidation;
use Mathielen\DataImport\Filter\ClassValidatorFilter;
use Symfony\Component\Validator\Constraints\NotBlank;
use Symfony\Component\Validator\Constraints\Regex;

$validator = ... //Symfony Validator

$validation = ValidatorValidation::build($validator)
  ->addSourceConstraint('salutation', new NotBlank()) //source field 'salutation' should not be empty
  ->addSourceConstraint('zipcode', new Regex("/[0-9]{5}/")) //source field 'zipcode' should be 5 digits
;

目标数据验证

ClassValidatorFilter

您可以使用ClassValidatorFilter将数据映射到对象树并验证对象(使用注释或不同配置的验证规则)。因此,您必须提供对象工厂。有一个JmsSerializerObjectFactory您可能想使用。

use Mathielen\ImportEngine\Validation\ValidatorValidation;
use Mathielen\DataImport\Filter\ClassValidatorFilter;
use Mathielen\DataImport\Writer\ObjectWriter\JmsSerializerObjectFactory;

$validator = ... //Symfony Validator

$jms_serializer = ... 
$objectFactory = new JmsSerializerObjectFactory(
  'Entity\Address',
  $jms_serializer);

$validation = ValidatorValidation::build($validator)
  ->setTargetValidatorFilter(new ClassValidatorFilter($validator, $objectFactory));

导入器

use Mathielen\ImportEngine\Importer\Importer;
use Mathielen\ImportEngine\Storage\ArrayStorage;

$ffsp = ...
$validation = ...
$targetStorage = ...

$array = array(1,2,3);
$importer = Importer::build($targetStorage)
  ->setSourceStorage(new ArrayStorage($array))
  ->validation($validation)
;

导入/源存储

您可以使用存储提供者(见上文)并设置选择ID,或者您可以直接使用特定的存储处理程序。

use Mathielen\ImportEngine\Storage\ArrayStorage;
use Mathielen\ImportEngine\Storage\LocalFileStorage;
use Mathielen\ImportEngine\Import\Import;
use Mathielen\ImportEngine\Importer\Importer;
use Mathielen\ImportEngine\Storage\Format\CsvFormat;

$targetArray = array();
$importer = Importer::build(new ArrayStorage($targetArray));
$import = Import::build(
    $importer,
    new LocalFileStorage(new \SplFileObject(__DIR__ . '/../../../metadata/testfiles/flatdata.csv'), new CsvFormat())
);

映射

有关映射的更多信息,请参阅原始文档

$import = ... 

$import->mappings()
  ->add('foo', 'fooloo')
  ->add('baz', array('some' => 'else'));
;

转换字段

有一些字段级别的内置转换器可用

  • upperCase
  • lowerCase
  • @TODO
$import = ...

$import->mappings()
  ->add('SALUTATION_FIELD', 'salutation', 'upperCase')
;

自定义字段级别转换

您必须将更复杂的转换器注册到导入器中,以便在导入时选择它们。

use Mathielen\ImportEngine\Mapping\Converter\Provider\DefaultConverterProvider;
use Ddeboer\DataImport\ValueConverter\CallbackValueConverter;
use Mathielen\ImportEngine\Import\Import;
use Mathielen\ImportEngine\Storage\ArrayStorage;
use Mathielen\ImportEngine\Importer\Importer;

$converterProvider = new DefaultConverterProvider();
$converterProvider
  ->add('salutationToGender', new CallbackValueConverter(function ($item) {
      switch ($item) {
        case 'Mr.': return 'male';
        case 'Miss':
        case 'Mrs.': return 'femaile';
      }
  }));

$targetStorage = ...

$importer = Importer::build($targetStorage);
$importer
  ->transformation()
  ->setConverterProvider($converterProvider);

$array = array();
$import = Import::build($importer, new ArrayStorage($array))
  ->mappings()
  ->add('salutation', 'gender', 'salutationToGender')
;

自定义行级别转换

与字段级别转换器一样,您必须首先注册您的转换器。

use Mathielen\ImportEngine\Mapping\Converter\Provider\DefaultConverterProvider;
use Ddeboer\DataImport\ItemConverter\CallbackItemConverter;
use Mathielen\ImportEngine\Import\Import;
use Mathielen\ImportEngine\Storage\ArrayStorage;
use Mathielen\ImportEngine\Importer\Importer;

$converterProvider = new DefaultConverterProvider();
$converterProvider
  ->add('splitNames', new CallbackItemConverter(function ($item) {
      list($firstname, $lastname) = explode(' ', $item['name']);

      $item['first_name'] = $firstname;
      $item['lastname'] = $lastname;

      return $item;
  }));

$targetStorage = ...

$importer = Importer::build($targetStorage);
$importer
  ->transformation()
  ->setConverterProvider($converterProvider);

$array = array();
$import = Import::build($importer, new ArrayStorage($array))
  ->mappings()
  ->add('fullname', null, 'splitNames')
;

导入运行器

为了运行配置的导入,您需要一个导入运行器。内部导入运行器构建一个工作流并运行它。您可以通过提供不同的工作流工厂来更改工作流构建的方式。

use Symfony\Component\EventDispatcher\EventDispatcher;
use Mathielen\ImportEngine\Import\Run\ImportRunner;
use Mathielen\ImportEngine\Import\Workflow\DefaultWorkflowFactory;
use Mathielen\ImportEngine\ValueObject\ImportConfiguration;
use Mathielen\ImportEngine\Storage\LocalFileStorage;
use Mathielen\ImportEngine\Storage\Format\CsvFormat;
use Mathielen\ImportEngine\Importer\ImporterRepository;

$import = ...

$importRunner = new ImportRunner(new DefaultWorkflowFactory(new EventDispatcher()));

//sneak peak a row
$previewData = $importRunner->preview($import);

//dont really write, just validate
$importRun = $importRunner->dryRun($import);

//do the import
$importRun = $importRunner->run($import);

导入运行统计信息

如果您使用默认的工作流工厂与导入运行器一起使用,您将从dryRun()和run()调用中获得基本统计信息。

$importRun = ...
$importRunner = ...

$importRunner->dryRun($import);
$stats = $importRun->getStatistics();

/*
Array
(
    [processed] => 1
    [written] => 1
    [skipped] => 0
    [invalid] => 0
)
*/

事件系统

您可以通过 Symfony 事件调度器 与正在运行的导入进行交互。

use Symfony\Component\EventDispatcher\EventDispatcher;
use Mathielen\ImportEngine\Import\Run\ImportRunner;
use Mathielen\DataImport\Event\ImportProcessEvent;
use Mathielen\DataImport\Event\ImportItemEvent;
use Mathielen\ImportEngine\Import\Workflow\DefaultWorkflowFactory;

$myListener = function ($event) {
    if ($event instanceof ImportItemEvent) {
    	$currentResult = $event->getCurrentResult(); //readonly access to current result in the process (might be false)
    }
};

$eventDispatcher = new EventDispatcher();
$eventDispatcher->addListener(ImportProcessEvent::AFTER_PREPARE, $myListener);
$eventDispatcher->addListener(ImportItemEvent::AFTER_READ, $myListener);
$eventDispatcher->addListener(ImportItemEvent::AFTER_FILTER, $myListener);
$eventDispatcher->addListener(ImportItemEvent::AFTER_CONVERSION, $myListener);
$eventDispatcher->addListener(ImportItemEvent::AFTER_CONVERSIONFILTER, $myListener);
$eventDispatcher->addListener(ImportItemEvent::AFTER_VALIDATION, $myListener);
$eventDispatcher->addListener(ImportItemEvent::AFTER_WRITE, $myListener);
$eventDispatcher->addListener(ImportProcessEvent::AFTER_FINISH, $myListener);

$workflowFactory = new DefaultWorkflowFactory($eventDispatcher);
$importRunner = new ImportRunner($workflowFactory);

$import = ...
$importRunner->run($import);

许可证

Import-Engine 采用 MIT 许可证发布。有关详细信息,请参阅 LICENSE 文件。