lucinda / migrations
轻量级PHP7.1库,用于执行跨环境迁移
Requires
- php: ^8.1
- lucinda/console: ~2.0
Requires (Dev)
- lucinda/unit-testing: ^2.0
This package is auto-updated.
Last update: 2024-09-25 21:22:30 UTC
README
目录
关于
此API作为一个平台,能够自动化在开发环境之间(例如,数据库结构的变化)进行数据迁移,这对于由持续集成/交付系统支持的系统非常有用。基于Symfony DoctrineMigrationsBundle 操作,它以截然不同的方式进行操作
- 像其他Lucinda库一样,它以简单性、灵活性和效率为指导思想(因此轻量)。它是完全独立的,没有捆绑到任何库或框架中,因此可以在使用或未使用Lucinda框架的任何PHP环境中使用
- 它就像一个骨架,需要在上面构建各种迁移模式(例如,SQL表的迁移)。这意味着它默认不会假设数据库需要迁移:任何需要在开发环境之间同步的内容都属于迁移范畴
使用的方法
在开发此API时,我自问:所有迁移,无论它们迁移什么,有什么共同之处?以下方面浮现在我的脑海中
- 迁移应该是一个能够在多个环境中执行一个或多个相关操作的类
- 类操作应该是 事务性的,这意味着它必须有COMMIT/ROLLBACK操作
- API需要能够在开发者请求时 生成 一个迁移类
- 开发者必须 编程 生成的迁移(例如,使用查询)
- API需要能够在 查找 和 执行 迁移
- API需要能够在环境之间 跟踪 迁移进度
- 跟踪需要将每个迁移类分配给一个状态:PENDING、FAILED、PASSED
- 一旦开发者在自己的环境中执行了迁移并确保一切正常,他必须 提交 它(例如,到GIT)
- 其他开发者拉取新更改,看到添加了新迁移,然后执行迁移以保持其环境更新
- 持续集成/交付系统(例如,TeamCity)将在每次构建时自动执行迁移,从而保持所有环境更新
此API仅执行上述API级别操作的物流,而不对以下内容做任何假设
- 迁移的主题是什么(例如,是否是SQL表相关?)
- 跟踪迁移进度的存储介质是什么(例如,是否是SQL表?)
支持的迁移操作将包括
- 生成迁移
- 运行所有迁移(其状态为PENDING或FAILED),相当于对每个迁移进行全局提交
- 提交单个迁移(其状态为PENDING或FAILED)
- 回滚单个迁移(其状态为PASSED)
请注意,与DoctrineMigrationsBundle不同,默认情况下不会支持 diff 和 dump-schema 操作,因为它们对 主题和实现假设(即SQL数据库是主题,DAO实现使用ORM模型)
实现
为了实现创建一个 迁移骨架 以进行特殊化的目标,此API仅定义了共同物流
- 缓存:存储和跟踪迁移进度的结构蓝图
- 脚本:支持提交/回滚操作的迁移类蓝图
- 结果:实现迁移(类操作)执行结果的类
- 状态:收集可能的迁移状态(待处理、通过、失败)的枚举
- 包装器:将上述四个组件绑定在一起,以便找到并执行迁移操作的类
- 控制台执行器:封装包装器以在控制台显示迁移操作结果的类
API完全遵循PSR-4规范,只需要PHP 8.1+解释器和控制台表API(用于在控制台显示迁移结果)。所有类都属于命名空间 Lucinda\Migration!要快速了解其工作原理,请查看
为确保可靠性,API已完全使用单元测试API进行单元测试,如测试文件夹中所示。要自行运行单元测试,请运行以下命令
cd vendor/lucinda/migrations
php test.php
安装
要安装此API,请转到项目根目录并运行
composer require lucinda/migrations
然后创建一个执行迁移的migrations.php脚本
require(__DIR__."/vendor/autoload.php"); // defines folder to store migrations and creates it if not exists $folder = "migrations"; if (!file_exists($folder)) { mkdir($folder); } // TODO: instance a Lucinda\Migration\Cache into $cache variable // run migrations based on console input $executor = new Lucinda\Migration\ConsoleExecutor($folder, $cache); $executor->execute((isset($argv[1])?$argv[1]:"migrate"), (isset($argv[2])?$argv[2]:""));
设置缓存
实现迁移缓存超出了骨架API的范围,该API不对迁移的主题或缓存的存储方式(例如:可以是MySQL,可以是Amazon DynamoDB)做出假设。
缓存实现的示例,绑定到SQL数据访问API,使用MySQL表存储信息
class TableCache implements \Lucinda\Migration\Cache { private $tableName; public function __construct(string $tableName) { $this->tableName = $tableName; } public function exists(): bool { return !empty(SQL("SHOW TABLES LIKE '".$this->tableName."'")->toRow()); } public function create(): void { SQL(" CREATE TABLE ".$this->tableName." ( id INT UNSIGNED NOT NULL AUTO_INCREMENT, class_name VARCHAR(255) NOT NULL, is_successful BOOLEAN NOT NULL DEFAULT TRUE, date TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY(id), UNIQUE(class_name) ) Engine=INNODB"); } public function read(): array { return SQL("SELECT class_name, IF(is_successful=0,2,3) AS status FROM ".$this->tableName."")->toMap("class_name", "status"); } public function add(string $className, int $statusCode): void { $isSuccessful = ($statusCode==\Lucinda\Migration\Status::PASSED); $results = SQL("UPDATE ".$this->tableName." SET is_successful=:status, date=NOW() WHERE class_name=:name", [ ":status"=>$isSuccessful, ":name"=>$className ])->getAffectedRows(); if ($results == 0) { SQL("INSERT INTO ".$this->tableName." SET is_successful=:status, class_name=:name", [ ":status"=>$isSuccessful, ":name"=>$className ]); } } public function remove(string $className): void { SQL("DELETE FROM ".$this->tableName." WHERE class_name=:name", [":name"=>$className]); } }
上述SQL函数的代码
function SQL(string $query, array $boundParameters = array()): \Lucinda\SQL\StatementResults { $preparedStatement = \Lucinda\SQL\ConnectionSingleton::getInstance()->createPreparedStatement(); $preparedStatement->prepare($query); return $preparedStatement->execute($boundParameters); }
执行
实现缓存后,您可以完成迁移脚本并执行迁移。使用上一节中的两个类的示例
require(__DIR__."/vendor/autoload.php"); // loads dependencies that bind Cache and Script to SQL Data Access API require(__DIR__."/TableCache.php"); require(__DIR__."/SQL.php"); // defines folder to store migrations and creates it if not exists $folder = "migrations"; if (!file_exists($folder)) { mkdir($folder); } // sets up Lucinda SQL Data Access API for current development environment based on XML new Lucinda\SQL\Wrapper(simplexml_load_file("xml/servers.xml"), getenv("ENVIRONMENT")); // run migrations based on console input, saving cache to SQL table "migrations" $executor = new Lucinda\Migration\ConsoleExecutor($folder, new TableCache("migrations")); $executor->execute((isset($argv[1])?$argv[1]:"migrate"), (isset($argv[2])?$argv[2]:""));
上述示例将在以下情况下工作
- 已设置一个名为“ENVIRONMENT”的环境变量,其值是您的开发环境名称
- mysql连接凭据已为当前开发环境设置,如SQL数据访问API配置部分中所述
- 当前数据库用户对目标模式(包括CREATE)拥有完全权限。如果不是这样,请手动运行create方法中的代码!
现在,您可以转到安装API的文件夹,执行generate命令,转到migrations文件夹并相应地填充up/down方法。一旦至少填充了一个迁移脚本,您将能够从控制台执行迁移。
为了最大化灵活性,不想使用ConsoleExecutor提供的控制台输出的开发者可以直接与Wrapper一起工作,并自行处理输出。
控制台命令
API允许以下控制台命令
生成命令是如何工作的
这将生成一个位于migrations文件夹中的迁移脚本类。打开该类,用查询填充up和down方法,如以下示例所示
class Version20210205105634 implements \Lucinda\Migration\Script { public function up(): void { SQL(" CREATE TABLE test( id INT UNSIGNED NOT NULL AUTO_INCREMENT, primary key(id) ) ENGINE=INNODB ") } public function up(): void { SQL("DROP TABLE test"); } }
示例
> php migrations.php generate
这将输出位于migrations文件夹中,您必须填充其up/down方法的脚本名称。
迁移命令是如何工作的
当运行migrate时,API将定位所有位于migrations文件夹中的脚本类,并将每个与缓存匹配
如果up没有抛出Throwable,则迁移将带有PASSED状态的保存到缓存中。否则,它将带有FAILED状态保存,并将Throwable消息显示在结果摘要中。
示例
> php migrations.php migrate
最后将是一个具有以下列的终端表格
up命令是如何工作的
当运行up(提交)命令时,API将首先检查作为第二个参数接收的脚本名称是否位于磁盘上的migrations文件夹中
示例
> php migrations.php up Version20210205105634
最终结果将是一个具有以下列的终端表格
down命令是如何工作的
当运行down(回滚)命令时,API将首先检查作为第二个参数接收的脚本名称是否位于磁盘上的migrations文件夹中
示例
> php migrations.php down Version20210205105634
最终结果将是一个具有以下列的终端表格
参考指南
本指南包括API使用的所有类、枚举和接口。
缓存
接口Lucinda\Migration\Cache定义了缓存必须实现的操作,该缓存用于保存迁移Lucinda\Migration\Script的执行进度。它定义了以下方法
脚本
接口Lucinda\Migration\Script定义了迁移脚本必须实现的操作,对应以下方法
如果发生错误,方法应该抛出Throwable,这将通知API它们以错误结束。以下建议适用
- 如果您需要在上下文内执行多个操作,它们必须进行事务处理(以确保数据完整性)
- 理想情况下,迁移应该只执行单个操作(以防止冲突)
结果
类 Lucinda\Migration\Result 封装了 Lucinda\Migration\Script 执行的结果。以下公共方法对开发者相关
通常情况下,您不需要使用这个类,除非您正在基于 Wrapper 构建 Wrapper 的结果显示器!
状态
枚举 Lucinda\Migration\Status 包含了迁移执行结果的 Result 状态列表
包装器
类 Lucinda\Migration\Wrapper 执行创建、定位和执行迁移 Script 的 API 任务,并更新 Cache。以下定义了以下公共方法
为了提高可重用性,API 不对结果的显示方式做任何假设,因此这个类严格上是一个模型,各种显示器都可以在其基础上构建!
ConsoleExecutor
类 Lucinda\Migration\ConsoleExecutor 假定您想封装 Wrapper 方法,以便使用映射命令在控制台/终端上显示其结果(更多信息)。它包含以下公共方法
当使用 execute 方法时
- $operation 值必须对应于 Wrapper 方法名(减去构造函数)
- $className 值必须只在 $operation 是 up / down 时存在