tanoconsulting / ibexa-migration-bundle
Kaliop eZ-Migration Bundle 到 Ibexa 4 的移植
Requires
- php: ^7.4|^8.0
- ext-ctype: *
- ext-json: *
- ext-pdo: *
- doctrine/dbal: ^2.11|^3.0
- ibexa/core: ^4.0
- mtdowling/jmespath.php: ^2.0.0
- nikic/php-parser: ^4.2.2
- symfony/expression-language: *
- symfony/process: *
- symfony/swiftmailer-bundle: *
- symfony/validator: *
- symfony/var-dumper: *
Requires (Dev)
- phpunit/phpunit: ^8.5.12
Suggests
- php-http/httplug-bundle: Required for usage of HTTP/CALL migration steps
Conflicts
README
用于 Ibexa 4 的 kaliop/ezmigrationbundle 的替代品。
此包使得以编程方式部署 Ibexa 数据库结构和内容变更变得简单。
它受到 DoctrineMigrationsBundle 的启发
非常感谢 Wizhippo 为启动此版本所做的工作,并且慷慨地捐赠了代码!
要求
-
PHP 7.4 或更高版本。
-
Ibexa 4.0(即 Ibexa DXP)。
对于 Ibexa DXP 3,请访问 tanoconsulting/ezmigrationbundle2。对于 eZPlatform 1-2 及更早版本,请访问 kaliop-uk/ezmigrationbundle。
安装
在 composer.json 文件中包列表的末尾,在 require
或 require-dev
下添加
composer require 'tanoconsulting/ibexa-migration-bundle:^1.0'
保存并运行
composer update --dev tanoconsulting/ibexa-migration-bundle
这将安装包及其所有依赖项。
然后请确保该包处于活动状态,通常通过编辑 config/bundles.php
检查包是否正确安装
如果您运行 php bin/console
,应该会在列表中看到以下新命令
kaliop
kaliop:migration:generate
kaliop:migration:mass_migrate
kaliop:migration:migrate
kaliop:migration:migration
kaliop:migration:resume
kaliop:migration:status
这表明该包已正确安装和注册。
更新包
要获取最新版本,您可以使用 composer
将包更新到最新版本
composer update tanoconsulting/ibexa-migration-bundle
有关从 kaliop/ezmigrationbundle 升级,请参阅 ezmigrationbundle_to_ibexamigrationbundle.md 中的说明。
入门
所有命令都接受标准的 Symfony/eZPlatform 选项,尽管其中一些可能对命令执行没有影响。
生成新的空迁移定义文件
该包提供了一个命令,可以轻松生成新的空白迁移定义文件。
例如
php bin/console kaliop:migration:generate --format=yml
上面的命令将在 ./src/
目录下的 MigrationsDefinitions
目录中放置一个新的 yml 模板文件。
如果该目录不存在,则命令将为您创建它。如果命令成功执行,它将创建一个名为以下模式的新 yml 文件:YYYYMMDDHHMMSS_placeholder.yml
。我们鼓励您重命名文件并将 placeholder
部分更改为更有意义的内容,但请保留时间戳部分和下划线,以及扩展名。
(Yaml 模板文件的內容以 Twig 模板形式存储)
列出所有迁移及其状态
要查看系统中所有可用的迁移定义以及它们是否已应用,只需在 eZPlatform 根目录中运行状态命令即可
php bin/console kaliop:migration:status
已应用迁移列表存储在名为 kaliop_migrations
的表中,该表位于数据库中。如果需要,包会自动创建该表。如果您想为该表使用不同的名称,可以更改 Symfony 参数 ez_migration_bundle.table_name
。
应用迁移
要在 eZPlatform 根目录中运行所有可用迁移,请执行 migrate 命令。
php bin/console kaliop:migration:migrate
注意:如果您刚刚执行了上述命令并收到错误消息,因为您刚刚生成的迁移定义文件无效,请不要担心——这是按设计进行的。请继续阅读下一段...
注意:迁移默认以 ID 为 14 的管理员用户执行。如果没有此用户账户在数据库中,您必须通过传递 -a
标志来指定使用另一个管理员账户。
应用单个迁移文件
要应用单个迁移,请运行 migrate 命令并传递其定义的路径,如下所示
php bin/console kaliop:migration:migrate --path=src/MyNamespace/MyBundle/MigrationVersions/20160803193400_a_migration.yml
注意:您还可以使用 --path
标志指定一个文件夹,在这种情况下,将执行该文件夹中包含的所有迁移定义。
编辑迁移文件
到目前为止一切顺利,但使用迁移实际上可以执行哪些操作呢?
每个迁移定义都由一系列步骤组成,其中每个步骤定义一个操作。
以下是一个简单的迁移示例,用于创建一个 'folder' 内容
-
mode: create
type: content
content_type: folder
parent_location: 2
attributes:
name: hello world
在 Yaml 迁移中,您可以执行以下类型的操作
- 创建、更新和删除 内容
- 创建、更新和删除 内容类型
- 创建、更新和删除 内容类型组
- 删除 内容版本
- 创建和删除 语言
- 创建、更新和删除 位置
- 创建、更新和删除 对象状态
- 创建、更新和删除 对象状态组
- 创建、更新和删除 角色
- 创建、更新和删除 部分
- 创建和删除 标签(来自 Netgen Tags Bundle)
- 创建和删除 URL 别名和 通配符
- 创建、更新和删除 用户
- 创建、更新和删除 用户组
- 从 废纸篓中清除和恢复内容
- 创建、追加、复制、重命名和删除 文件
- 执行SQL查询
- 执行命令行脚本
- 执行Symfony服务的方法
- 执行PHP函数和静态方法
- 执行HTTP调用
- 发送电子邮件
- 遍历数组并在每个元素上执行上述操作之一
- 取消、暂停或挂起迁移本身
- 生成并保存新的迁移定义
所有支持的迁移语言特性的详细信息请参见DSL语言描述
自定义迁移
对于更具体的需求,您还可以使用两种其他类型的迁移
- SQL迁移
- PHP迁移
SQL迁移
生成SQL迁移定义的示例命令
php bin/console kaliop:migration:generate create-new-table --format=sql
这将创建以下文件,您可以自由编辑
./src/MigrationsDefinitions/2021XXYYHHMMSS_mysql_create-new-table.sql
注意 如果您重命名sql文件,请记住,应该应用该数据库的类型是文件名中第一和第二个下划线字符之间的部分。如果您稍后尝试在运行PostgreSQL的eZPublish安装上执行该迁移,迁移将失败。当然,您也可以为不同类型的数据库创建特定的SQL迁移。
迁移包本身不对支持的数据库类型施加限制,但它基于Doctrine DBAL,因此它只能在工作在 Doctrine 支持的数据库上。
注意 您也可以将执行的SQL语句保存为yml格式的迁移文件。这为您提供了更多选项,例如设置和解析引用。Yml格式的迁移文件不需要在名称中包含数据库类型。
注意 如果迁移中的SQL语句(或语句)太长,迁移可能会失败或仅部分应用,在某些情况下(例如使用MySQL)甚至不会报告错误。如果您需要执行多个长查询,您最好将它们拆分,要么在多个.sql迁移中,要么在一个.sql步骤的单一.yml迁移中。
PHP迁移
如果您需要进行的操作类型对于YML或SQL来说太复杂,您可以使用PHP类作为迁移定义。要生成PHP迁移定义,请执行
php bin/console kaliop:migration:generate AMigrationClass --format=php
这将创建以下文件,您可以自由编辑
./src/MigrationsDefinitions/2021XXYYHHMMSS_AMigrationClass.php
如您在生成的定义中所见,用于迁移的PHP类需要实现特定接口。将Symfony DIC容器传递给迁移类,以便它可以访问所有所需的服务、参数和其他内容。
要查看使用PHP完成的迁移定义的更详细示例,请查看该包的MigrationVersions文件夹。
注意 如果您重命名php文件,请记住,文件名和它包含的类的名称是相关的 - 应用程序的标准自动加载机制在加载迁移定义时并不适用。这也是为什么用作迁移的PHP类不应该使用命名空间的原因。
注意 还可以通过在yaml迁移中将其声明为迁移步骤来运行任何现有的Symfony服务方法。有关详细信息,请参阅相关DSL。
注意 还可以通过在yaml迁移中将其声明为迁移步骤来运行任何现有的PHP函数或静态类方法。有关详细信息,请参阅相关DSL。
重新执行失败的迁移
重新执行状态为“失败”的迁移的最简单方法是从迁移表中删除它
php bin/console kaliop:migration:migration migration_name --delete
从迁移表中删除有关迁移的信息后,运行migrate
命令将再次执行它。
事务/回滚更改的使用
默认情况下,该bundle在每个迁移中运行数据库事务。这意味着如果某个步骤失败,所有先前的步骤都会回滚,数据库将保持其原始状态。这是设计时内置的安全功能;
- 如果您希望迁移步骤在单独的事务中执行,最简单的方法是为每个步骤创建一个单独的迁移文件
- 您可以使用命令行标志
-u
来禁用migrate命令的事务使用
请注意,默认情况下,migrate
命令在遇到第一个失败的迁移时停止,但可以通过标志执行,允许它在出现故障的情况下继续并执行所有可用的迁移。
至于回滚更改:鉴于eZPublish API的性质,回滚内容更改并非易事。因此,该bundle不提供内置支持以回滚数据库到应用给定迁移之前的版本。我们建议在应用迁移之前始终进行数据库快照,并在需要回滚更改时使用它。另一种方法是在迁移中编写单独的迁移来撤销更改。
通过事件监听器自定义迁移逻辑
将自定义逻辑挂钩到迁移执行的一个简单方法——而无需实现自己的自定义操作执行器——是使用事件监听器。
在迁移执行期间,为每个步骤触发两个事件
* ez_migration.before_execution => listeners receive a BeforeStepExecutionEvent event instance
* ez_migration.step_executed => listeners receive a StepExecutedEvent event instance
仅当迁移由于步骤抛出特定的迁移中止异常而失败时,才会触发事件
* ez_migration.migration_aborted => listeners receive a MigrationAbortedEvent event instance
当使用kaliop:migration:generate
命令生成迁移时,将触发事件,允许修改将序列化为迁移步骤的数据
* ez_migration.migration_generated => listeners receive a MigrationGeneratedEvent event instance
为了处理这些事件,您需要声明标记的服务,例如:
my.step_executed_listener:
class: my\helper\StepExecutedListener
tags:
- { name: kernel.event_listener, event: ez_migration.step_executed, method: onStepExecuted }
和相应的PHP类
use Kaliop\eZMigrationBundle\API\Event\StepExecutedEvent;
class StepExecutedListener
{
public function onStepExecuted(StepExecutedEvent $event)
{
// do something...
}
}
事件订阅者是事件监听器的替代方案,这是Symfony项目中的标准做法。
已知问题和限制
-
与Doctrine Migrations Bundle不同,此bundle不支持更改回滚。请参阅上述内容了解原因。
-
如果您正在使用Doctrine Migrations Bundle来管理您的模式,您将获得处理Kaliop Migrations Bundle数据库表的意外SQL。目前,最佳解决方案是在运行
doctrine:migrations:diff
及其相关命令时使用命令行上的filter-expression
参数,值为kaliop_migrations_*
-
在运行迁移时遇到致命错误可能发生在您使用Solr搜索引擎Bundle的情况下。在这种情况下,问题更加严重,因为即使节点或对象在数据库事务内发送到Solr,Solr搜索索引可能配置为仅在短时间延迟后提交接收到的数据。一种已知的工作区涉及
- 将迁移步骤分开成单独的迁移
- 通过使用
migrate
命令的-p
标志在每个事务(和进程)中运行迁移 - 在迁移2 .. N中添加
sleep
迁移步骤 - 和/或配置Solr以始终立即提交索引更改(例如,禁用
commitwithin
)
-
当在多核心配置中使用SOLR并遇到
java.lang.NegativeArraySizeException
错误时,您必须将参数ez_migration_bundle.query_limit
的值设置得低于默认值2147483647 -
如果您在运行涉及大量内容更改的迁移时未收到任何错误消息却收到致命错误,例如更改具有许多内容的contentType,那么可能是您的php进程内存不足。已知解决方案包括
- 通过运行带有选项'-d memory_limit=-1'的php脚本来增加允许的最大内存量
- 使用具有减少日志记录和禁用内核调试的Symfony环境执行迁移命令:已知
dev
环境默认配置会泄漏内存 - 尽可能将迁移转换为一个在循环中逐个加载和修改内容,而不是一次性修改所有内容的迁移。请参阅使用循环的示例此处。
-
如果您收到包含消息'您不能创建一个处于非活动范围("request")的服务("request")'的致命错误,请查看以下问题以获取可能的解释和解决方案思路:https://jira.ez.no/browse/EZP-24691
-
当更新角色时,您必须在迁移中指定其所有策略。任何不在yml文件中的现有策略将被删除。为了便于创建更新角色的迁移,请使用带有
--type=role
标志的migration:generate
命令 -
在创建内容类型时要小心:迁移包内部使用的eZPublish API将允许您在内容类型标识符中使用破折号,即使生成的内容类型将变得不可用,例如。
无效定义示例
type: ezstring name: Topbar-hover-color identifier: topbar-hover-color
-
当eZ以集群模式设置时,如果您正在设置对ezimage、ezbinaryfile或ezmedia类型的内容字段路径的引用,或生成创建/更新它的迁移,则您将得到的路径不是磁盘上的绝对路径,而是相对于'nfsvar'目录的路径,这使得它不适合直接在例如内容/创建迁移中使用。请查看Cookbook中的示例以了解如何处理此问题。
常见问题
我如何更新开发、测试和生产环境中具有不同ID的特定内容?
A: 使用'reference/set'迁移步骤来定义所需内容ID的引用,并使用Symfony参数为每个Symfony环境存储不同的值。例如
-
type: reference
mode: set
identifier: content_id_ref
value: '%a.parameter.name%'
-
type: content
mode: update
match:
content_id: "reference:content_id_ref"
etc: ...
请注意,还有许多其他解决方案可以解决这个问题,例如确保您的目标内容和位置在所有环境中都具有相同的Remote_id,或将引用值作为选项传递给migrate
命令行。
如何更新现有角色以更改其策略?
当使用迁移来更新角色时,您必须定义其所有策略。任何未定义的策略将被删除。确保您不会忘记任何现有策略的最安全和最简单的方法是首先生成一个具有角色当前完整指定的更新迁移,然后手动编辑。
创建此类迁移的示例命令
php bin/console kaliop:migration:generate --type=role --mode=update --match-type=identifier --match-value=Anonymous bundleName
当通过generate
命令将内容导出到yml迁移时,属性列表为空
A: 这很可能是由于使用了不良的语言配置
是否有实现需要复杂迁移的常见任务的示例?
A: 有,请查看文件夹 Resources/doc/Cookbook/
我可以在迁移过程中运行外部工具(命令行脚本)吗?
A: 当然可以。请查看相关的 dsl 和 cookbook 示例 以获取详细信息。
扩展包
支持自定义迁移
该包已被设计成易于以多种方式扩展,例如
- 添加对自定义/复杂字段类型的支持
- 添加对 Yml 定义中完全新动作的支持
- 添加对存储迁移定义的新文件格式的支持
- 添加对迁移定义中自定义引用的新解析器的支持
- 接管迁移定义从文件系统加载或存储到数据库的方式
- 等等...
遵循 Symfony 最佳实践,对于上述列表中的前 4 个选项,您只需要创建一个服务并给它一个适当的标签(实现服务的类当然应该实现适当的接口)。
要找出您需要实现的标签的名称,以及您可以覆盖的所有其他服务,请查看 services.yml 文件。
还可以定义自定义事件监听器/订阅者来扩展迁移执行逻辑。请参阅上面专门的段落以获取更多详细信息。
运行测试
该包使用 PHPUnit 运行功能测试。
在工作的 eZPublish / eZPlatform 安装上运行测试
要运行测试
export KERNEL_CLASS=App\Kernel (or whatever you renamed it to)
export APP_ENV=behat (or whatever your environment is)
bin/phpunit --stderr -c vendor/tanoconsulting/ibexa-migration-bundle/phpunit.xml.dist
注意 测试 不会 模拟与数据库的交互,但在其中创建/修改/删除许多类型的数据。因此,运行测试可能会留下过时/损坏的数据。建议使用专门的 eZPublish 安装或至少专门的数据库运行测试套件。
为包设置专门的测试环境
运行包的测试的一个更安全的选项是设置一个类似 GitHub Actions 上运行测试套件时使用的专门环境。优点是多方面的:一方面,您可以开始使用您想要的任何版本的 eZPublish;另一方面,您将更有信心您添加或修改的任何测试也将通过 GitHub。缺点是您将需要 Docker 和 Docker-compose,并且您将使用的环境将与标准的 eZPublish 设置大不相同!此外,构建环境将需要大量的磁盘空间和时间。
设置专门的测试环境并在其中运行测试的步骤
git clone --depth 1 https://github.com/tanoconsulting/euts.git teststack
# if you have a github auth token, it is a good idea to copy it now to teststack/docker/data/.composer/auth.json
# this config sets up a test environment with Ibexa 4.2 running on php 8.0 / ubuntu jammy
export TESTSTACK_CONFIG_FILE=Tests/environment/.euts.4.2.env
./teststack/teststack build
./teststack/teststack runtests
./teststack/teststack stop
您还可以运行单个测试用例
./teststack/teststack runtests ./Tests/phpunit/01_CollectionsTest.php
注意:第一次运行时这需要一些时间,但在后续运行中会更快。注意:请确保有足够的磁盘空间可用。
如果您想手动运行命令,例如 symfony 控制台
./teststack/teststack console cache:clear
或者轻松访问数据库 shell 提示符
./teststack/teststack dbconsole
或者命令行 shell 提示符到运行测试的 Docker 容器
./teststack/teststack shell
Docker容器中的测试使用文件Tests/environment/.euts.4.2.env
中指定的debian/php/eZPlatform版本运行,该文件由环境变量TESTSTACK_CONFIG_FILE
指定。如果该环境变量未设置值,则查找名为.euts.env
的文件。如果不存在此类文件,则使用一些默认值,您可以在./teststack/README.md文档中查看它们是什么。如果您想测试eZ/php/mysql/debian的不同版本,请随意
- 创建一个
.euts.env
文件,如果尚不存在 - 向其中添加任何所需的变量(参考文件
teststack/.euts.env.example
作为指导) - 重新构建测试堆栈
- 以常规方式运行测试
您甚至可以使用不同的env文件,同时保持多个测试堆栈并行可用,例如
- 创建一个文件
.euts.env.local
,并向其中添加任何所需的env变量,以一个独特的COMPOSE_PROJECT_NAME
开始 - 通过
./teststack/teststack. -e .euts.env.local build
构建新的测试堆栈 - 通过:
./teststack/teststack -e .euts.env.local runtests
运行测试
我们的向后兼容性承诺
此包遵循语义版本控制原则。
然而,向后兼容性有许多不同的口味。实际上,几乎每一次变更都可能破坏一个应用程序。例如,如果我们向一个类中添加一个新方法,这将破坏扩展了该类并添加了相同方法(但方法签名不同)的应用程序。
本节专门用于详细解释哪些指南决定了为每个新版本的包增加主/次/修订号的选择。
对于使用该包的开发者
通过以下方式实施语义版本控制的遵守
- 现有的迁移将继续在所有次要和修订版本增加中工作
- 新迁移步骤和新支持的现有迁移步骤元素可能会在次要版本中引入,但在修订版本中不会引入
- 任何针对删除的迁移步骤或步骤元素将首先至少在一个次要版本中被弃用。在这种情况下,该元素将立即从DSL文档中删除,并在代码库中添加
@deprecated
或BC
注释 - CLI命令的语法将继续在所有次要和修订版本增加中工作
- 现有CLI命令的新选项和新命令可能会在次要版本中引入,但在修订版本中不会引入
- 现有CLI命令的文本输出可能会在任何版本中更改 - 请不要依赖于它具有固定格式以解析它
- 由包发出的事件将继续在所有次要和修订版本增加中工作
- 可能会在次要版本中引入新事件,但在修订版本中不会引入
对于扩展或修改该包的开发者
在包的内部结构方面,即PHP类和Symfony服务方面,事情有点模糊。尽管我们尽力避免破坏扩展现有类或重新定义现有服务的用户代码,但这并不总是可能的。
- 任何可能对扩展或修改包的开发者产生潜在影响的变化不应发生在修订版本中,但可能会发生在次要版本中。只有当有充分的理由这样做(即,大多数情况下,这是为了修复错误或使重要新功能的实现成为可能)时,才会进行此类更改,并在NEWS文件中记录。