salaun / laravel-complex-upsert
一个无需依赖主键即可高效更新数据库的工具。
Requires
- php: ^8.0
- illuminate/contracts: ^8.37
- spatie/laravel-package-tools: ^1
- tpetry/laravel-postgresql-enhanced: ^0.23.0
Requires (Dev)
- orchestra/testbench: ^6
- pestphp/pest: ^1.18
- pestphp/pest-plugin-laravel: ^1.1
- pestphp/pest-plugin-mock: ^1.0
- pestphp/pest-plugin-parallel: ^0.3.1
- pestphp/pest-plugin-watch: 1.x-dev
- spatie/pest-plugin-test-time: ^1.0
- vimeo/psalm: ^5
This package is auto-updated.
Last update: 2024-10-02 14:37:40 UTC
README
典型用例是更新数据库,使用基于文本的文件数据树(json 或 XML)。
在这种情况下,对象可能没有主键,因此我们通过属性组合匹配数据以唯一定位它们。
主要原则是
- 在造成永久性损害之前失败。如果可能,删除损坏部分并更新其余部分。由 Laravel 验证辅助程序和引用集合提供支持。
- 多线程得益于 Laravel 队列系统。
- 允许与数据块进行部分集成 哈希
- 保留相同的记录及其 ID。不删除并从头开始创建,归功于 SQL upsert 查询。
- 重用 模型关系网络 以确保项目一致性和维护,避免代码重复。
- 通过分组引用获取(引用集合)和按模型类型(upsert 服务)进行 upsert 以尽可能减少数据库查询。
- 可定制、可读性强且易于维护。目的是引导您进行结构化和组织化的数据库更新过程,同时使设置此包实现的工作状态变得简单。
安装
您可以通过 composer 安装此包
composer require salaun/laravel-complex-upsert
您可以使用以下命令发布配置文件
php artisan vendor:publish --provider="Salaun\ComplexUpsert\ComplexUpsertServiceProvider" --tag="laravel-complex-upsert-config"
这是已发布配置文件的内容
return [
'chunk_size' => env('COMPLEX_UPSERT_CHUNK_SIZE', 300),
];
Lexic
- 关系:子节点对父节点的关系。
- 引用:数据库中已存在的记录。数据树的最后节点。
- Upsert(up[date or in]sert):在 SQL 上下文中表示插入记录的操作,如果它已存在,则更新它。在此包中是集成和更新的同义词。
- 反向关系:为了处理递归 upsert,父节点需要先 upsert 以获取其 ID 并将其设置为子节点的外键。这意味着我们需要在设置外键时知道父节点与其子节点之间的关系。因此,我们设置关系名称 parent_relation::child_relation,以避免与父节点本地的 Laravel 关系混淆。
要求
数据库
此包旨在与 PostgreSQL 数据库一起使用(感谢 Thomas Pettry 包),但也应与 MySQL 数据库一起工作。
关系
在处理过程中使用的关系应从两侧定义,并且模型必须 使用 相关特质。
外键关系
嵌套模型必须定义所需的 DELETE ON CASCADE
。
使用方法
建议您遵循此包提供的流程,也可以不使用作业“框架”调用服务方法。
此包中包含的测试也是实现示例。
警告
键
根据 Laravel 文档 Laravel documentation,您应该注意您的迁移
All databases systems except SQL Server require the columns in the second argument provided to the upsert method to have a "primary" or "unique" index.
因此,您应该参考创建索引部分
$table->unique(['account_id', 'created_at'], 'upsert_key');
复合唯一键注意事项
当复合唯一键包含可空列且项目值为NULL时,upsert操作将导致重复记录。这是由于SQL标准不识别此复合键为唯一键。
UNIQUE索引允许包含NULL的列有多个NULL值。(MySQL文档)
在PostgreSQL中,可以使用UNIQUE INDEX WITH NULL DISTINCT
来避免此问题。
只有引用才能有从左到右的关系
考虑以下情况products <= product_label => labels => entities
。目前不支持此情况。标签必须设置为引用,但实体无法自动更新。
软件包技术细节
辅助工具
此软件包包含名为服务的辅助工具,它们负责处理
- upsert服务:负责将数据(如果需要,以递归方式)插入。
- upsert引用服务:负责从数据库检索已存在的数据,以便完成要插入的模型。这些数据可用于将传入数据与要upsert的记录匹配。
- WhereInMulti()查询构建器宏。基于多列/值执行whereIn查询。
SELECT * FROM `users` WHERE (`firstname`, `lastname`) IN (('micheal', 'bay'), ('steven', 'spielberg'));
模型配置
要upsert的模型需要配置,以便服务可以使用它们。这是通过使用特性完成的。一些配置是静态参数,需要设置在要upsert的模型上
- canFail (bool):定义此记录是否可以安全地从数据集中删除。
- upsertId (array):用于数据库匹配的哪些列。
- upsertColumns (array):在基表中已有数据记录的情况下,应更新哪些列。3个选项:"[]" 什么都不更新,"null" 更新所有内容,"['firstname', 'lastname']" 更新firstname和lastname列。
- rules (array):用于创建Laravel模型的Laravel验证辅助工具时使用的数组。
引用集合
引用集合受laravel-media-library的启发,它根据声明性函数定义引用行为。如果upsert模型有引用,则必须声明集合才能检索和解析它。此类集合包含
- relation (string):用于在upsert模型上设置外键的关系。
- referenceId (array):用于获取(和更新)引用的哪些列。
- query (Query Builder):访问数据库内容的查询。
- syncColumns (array):如果需要,在获取其ID之前更新哪些引用值。
工作流程
除了使upsert更容易之外,还有一个关于处理应如何进行的实现。基于Laravel队列和批处理系统,我们引入了一系列步骤,以确保将正确数据放在正确位置,同时不风险破坏数据库内容的一致性。
分发器
分发器的角色是将upsert的片段分发给处理器。可以是下载并提取id块的ZIP文件,以便处理器作业的大小大致相同。对于给定的父项,它将用要异步执行的处理器作业填充其批处理。还有一个选项,在所有批处理作业执行完毕后执行操作(例如,删除新数据中不存在的记录)。
处理器
处理器将积极执行upsert。通过一系列步骤进行,它会过滤掉不可用或不相关的数据,并将其余部分格式化以完成upsert状态。当关系被静默遗忘时,会触发日志,而统计允许我们跟踪失败记录的位置和数量。致命失败由队列系统处理,并根据配置进行记录和重试。
存在系统步骤
- beforeUpsert:在执行 confirmedUpsert() 步骤之前准备数据。
- afterUpsert:一旦我们确认模型已成功插入,即保存数据供 confirmedUpsert() 步骤使用。
用户步骤
- getData:从您选择的源获取原始数据。无论它是API、XML或JSON文件还是数据库导出。
- instanciateData:创建与原始数据结构匹配的基本PHP对象。仅执行数据类型检查。
- confirmedUpsert:在此处决定数据是否值得更新,或者是否可以忽略。
- transformData:允许我们将 instanciateData 的基本PHP对象转换为laravel Model。可以进行文本格式化、结构更改等操作。还可以利用 Laravel 验证助手 makeValid() 来确保数据可以无问题地存入数据库。
- resolveReferences(使用 UpsertReferenceService):如果数据库中缺少某些引用,则提供更新数据库中已有引用的选项,获取所需内容并对外键模型应用所需更改。
- upsertModels(使用 UpsertService):如果有缺失(甚至在新数据中缺少某些关系并且您希望匹配时删除),则更新或插入,以更新数据库。如果设置了“反向关系”,则将以递归方式执行upsert。
可以添加任意数量的自定义步骤。任何未通过步骤的元素必须设置为null并从数组中过滤。可以将关系模型的模型类型设置为"canFail()",这将忽略它而不会取消其父元素的upsert。
优化
- 明智地排序唯一索引中的列 来源
测试
composer p
变更日志
请参阅 变更日志 了解最近更改的详细信息。
致谢
许可证
MIT许可证(MIT)。请参阅 许可证文件 了解更多信息。