stickee/import

Stickee数据导入模块 - 从CSV、Akeneo等导入

2.0.0 2020-12-03 14:07 UTC

This package is auto-updated.

Last update: 2021-02-03 15:39:03 UTC


README

从Akeneo、CSV数据源、Hydrogen或其他来源导入数据到数据库表。

安装

composer require stickee/import

操作方法

导入器遵循以下流程

  1. 创建一个临时表
  2. 将数据源中的数据插入到表中
  3. 将临时表合并到数据表中

用法

创建Importer对象的实例并调用run()

use Stickee\Import\Importer;

$importer = new Importer($dataMerger, $temporaryTableManager, $iterable);
$importer->run();

Importer构造函数的三个参数如下。您可以选择在run前后分别手动调用initialise和/or cleanup - 请参阅多个导入器

数据合并

系统提供了一个可创建实例的单一\Stickee\Import\Utils\DataMerger类。

构造函数

您可以使用以下构造函数参数或使用下面的流畅方法来构建DataMerger。

名称 类型 默认值 描述 示例
$db \Illuminate\Database\ConnectionInterface - 数据库连接
$tableName string - 目标表名 my_data
$tempTableName string - 临时表名 - 通常您可以从$temporaryTableManager->getTableName()获取此信息 my_data_tmp
$columns array - 要插入数据的列 ['code', 'name', 'description']
$importIndexes string[] ['PRIMARY'] 导入时使用的索引。通常这将是一个或多个包含$joinFields的索引 ['PRIMARY', 'code']
$joinFields array ['id'] 用于连接表和临时表的列 ['code']
$preservedColumns array [] 应插入但未更新的列;如果可以在导入器外部写入表,则使用 ['custom_description']
$deleteRemovedItems boolean true 删除不在临时表中的条目 false
$addAutoIdColumn boolean true 是否添加{table}.id自动编号字段。除非数据中包含ID,否则应为true false

属性

您可以设置这些属性以微调行为

名称 类型 描述
$bulkInsertSize int 每次插入的行数
$bulkUpdateSize int 每次删除/更新的行数

流畅方法

方法 描述
setImportIndexes(string $importIndexes) 设置导入索引
setJoinFields(string $joinFields) 设置连接字段
setPreservedColumns(string $preservedColumns) 设置保留列
setDeleteRemovedItems(string $deleteRemovedItems) 设置删除已删除的项
setAddAutoIdColumn(string $addAutoIdColumn) 设置添加自动ID列
setBulkInsertSize(int $bulkInsertSize) 设置批量插入大小
setBulkUpdateSize(int $bulkUpdateSize) 设置批量更新大小
setOutput(?\Illuminate\Console\OutputStyle) 设置控制台输出

例如

$dataMerger = (new DataMerger($db, 'my_table', $temporaryTableManager->getTableName(), `['code', 'name', 'description']))
    ->setImportIndexes(['PRIMARY', 'my_table_code_unique'])
    ->setJoinFields(['code'])
    ->setBulkInsertSize(10000);

临时表管理器

这将创建和删除临时表。

通常 Stickee\Import\TableManagers\AutoTableManager 是最佳选择。这将尽可能使用 TEMPORARY TABLE ENGINE=MEMORY 表,并回退到 TEMPORARY TABLE

可迭代对象

这必须是一个提供关联数组(列名 => )的可迭代对象。实现这一点由你决定,但有一些类可以帮助创建 Akeneo 数据和 Hydrogen Table Sync 数据的迭代器。对于 Akeneo,你可以使用 \Stickee\Import\Iterators\AkeneoIterator。对于 Hydrogen,你可以使用或扩展 \Stickee\Import\Iterators\TableIterator

示例 1

迭代器可以非常简单,只是一个普通的数组

$iterator = [
    ['name' => 'Fluttershy', 'colour' => 'yellow'],
    ['name' => 'Rainbow Dash', 'colour' => 'blue'],
    ...
];

示例 2

从 CSV 文件读取可以实现为一个生成器,以降低内存使用

use IteratorAggregate;
use League\Csv\Reader;
use Stickee\Import\Utils\RowMapper;

$iterator = new class implements IteratorAggregate
{
    public function getIterator(): iterable
    {
        $csvStream = (new FileStream())->getStream('http://example.com/data.csv');
        $csv = Reader::createFromStream($csvStream);
        $rowMapper = new RowMapper(['name' => 0, 'colour' => 1]);

        foreach ($csv as $row) {
            yield $rowMapper->mapRow($row);
        }
    }
}

图片

如果您需要下载图片,则应创建类似于上述 CSV 示例的生成器,并在生成行之前下载每行的图片。有辅助实用程序可以帮助简化下载过程。

辅助实用工具

DownloadManager

下载文件,记录已下载的文件,以避免重复下载同一文件

use Stickee\Import\Utils\DownloadManager

$downloadManager = new DownloadManager($fileDownloadService);
$downloadManager->downloadFile('http://example.com/1.jpg');
$downloadManager->downloadFile('http://example.com/2.jpg', 'two.jpg');

FileDownloadService

将文件下载到 Laravel 磁盘。它接受本地路径以及 URL

use Stickee\Import\Utils\FileDownloadService

$fileDownloadService = new FileDownloadService($disk, $fileStream);
$fileDownloadService->storeFileContents('a.txt', 'Hello World!');
$fileDownloadService->storeFile('http://example.com/a.txt', 'example-a.txt');
$fileDownloadService->storeFile('/var/www/a.txt', 'local-a.txt');

FileStream

将本地路径或 URL 打开为文件流。完成时不要忘记 fclose 流。

use Stickee\Import\Utils\FileStream

$fileStream = new FileStream();

$file = $fileStream->getStream('http://example.com/a.txt');
echo fread($file, 1024);
fclose($file);

IdLookupService

如果您正在向数据添加自己的主键,那么您可能需要使用数据自己的键(称为 代码)来查找关系。

例如,您有两个 CSV 文件

ponies.csv

code,tribe,name,colour
F,P,Fluttershy,yellow
RD,P,Rainbow Dash,blue
TS,U,Twilight Sparkle,purple

tribes.csv

code,name
P,Pegasus
U,Unicorn
E,Earth Pony

您的数据库看起来像这样

mydb.ponies

id | code | tribe_id | name             | colour
---|------|----------|------------------|-------
 1 | F    | 1        | Fluttershy       | yellow
 2 | RD   | 1        | Rainbow Dash     | blue
 3 | TS   | 2        | Twilight Sparkle | purple

mydb.tribes

id | code | name
---|------|-----------
1  | P    | Pegasus
2  | U    | Unicorn
3  | E    | Earth Pony

对于 mydb.ponies 表,您需要将 CSV 中的 tribe 转换为 mydb.tribes.id 值 - 例如,P 应转换为 1。这可以通过创建一个 IdLookupService 并在迭代器中使用它来实现。

use Stickee\Import\Utils\IdLookupService

$idLookupService = new IdLookupService($db, 'tribes', 'code');
$tribeId = $idLookupService->get('P'); // $tribeId === 1

QueryChunkGenerator

这与 Laravel 的 DB::query()->chunk() 类似,但它产生每个记录而不是接受回调函数

use Stickee\Import\Utils\QueryChunkGenerator

$query = MyModel::query();
$iterator = new ChunkGenerator($query, 100);

// This will echo the ID for all models, but will only fetch 100 at a time from the database
foreach ($iterator as $row) {
    echo $row->id;
}

RowMapper

这是一个辅助工具,用于将数字索引数据转换为关联数组,例如用于 CSV 数据。CSV 行索引不需要全部指定,也不需要任何特定的顺序。如果您想添加一个空列,将索引设置为 -1

use Stickee\Import\Utils\RowMapper

$map = [
    'id' => 0,
    'colour' => 4,
    always_null' => -1,
];

$rowMapper = new RowMapper($map);

$row = $rowMapper->mapRow([1, 'F', 1 , 'Fluttershy', 'yellow']);

// $row === ['id' => 1, 'colour' => 'yellow', 'always_null' => null]

多个导入器

如果您想在单个事务中运行多个导入器,您必须在开始事务之前运行 initialise(),否则它将被自动提交。

$importer1->initialise();
$importer2->initialise();

DB::transaction(function () {
    $importer1->run();
    $importer2->run();
});

常见错误

SQLSTATE[HY000]: 一般错误:1114 表 '...' 已满

当使用内存表(在可能的情况下将由 MemoryTableManagerAutoTableManager 使用)超过 MySQL 的 max_heap_table_size 时,这种情况会发生。如果您遇到此错误,您可以使用 TemporaryTableManager,它将使用磁盘上的临时表,或者像这样增加 max_heap_table_size

$maxHeapTableSize = DB::select(DB::raw('SHOW VARIABLES LIKE "max_heap_table_size"'))[0]->Value;
DB::statement('SET SESSION max_heap_table_size=4294967295');

$importer->run();

DB::statement('SET SESSION max_heap_table_size=?', [(int)$maxHeapTableSize]);

开发

修改项目最简单的方法是将导入模块的项目设置为从您的文件系统而不是从 composer 仓库加载模块,如下所示

  1. composer remove stickee/import
  2. 编辑 composer.json 并添加
    "repositories" : [
            {
                "type": "path",
                "url": "../import-module"
            }
        ]
    其中 "../import-module" 是您检出此项目的路径
  3. composer require stickee/import

注意: 不要这样提交您的 composer.json