tobento / service-migration
适用于任何PHP应用的迁移管理器。
1.1.2
2024-08-13 14:00 UTC
Requires
- php: >=8.0
- psr/container: ^2.0
- tobento/service-autowire: ^1.0
- tobento/service-file-creator: ^1.0
- tobento/service-filesystem: ^1.0
Requires (Dev)
- phpunit/phpunit: ^9.5
- tobento/service-container: ^1.0
- vimeo/psalm: ^4.0
README
迁移服务提供了一种灵活的方式来处理PHP应用的迁移。
目录
入门
运行此命令以添加运行中的迁移服务项目最新版本。
composer require tobento/service-migration
要求
- PHP 8.0或更高版本
亮点
- 框架无关,适用于任何项目
- 解耦设计
简单示例
创建迁移
namespace App\Blog; use Tobento\Service\Migration\MigrationInterface; use Tobento\Service\Migration\ActionsInterface; use Tobento\Service\Migration\Actions; use Tobento\Service\Migration\Action\FilesCopy; use Tobento\Service\Migration\Action\FilesDelete; use Tobento\Service\Migration\Action\DirCopy; use Tobento\Service\Migration\Action\DirDelete; /** * BlogMigration */ class BlogMigration implements MigrationInterface { /** * Return a description of the migration. * * @return string */ public function description(): string { return 'Blog migration.'; } /** * Return the actions to be processed on install. * * @return ActionsInterface */ public function install(): ActionsInterface { return new Actions( new FilesCopy( files: [ 'dir/to/store/config/' => [ 'dir/blog/config/blog.php', ], ], description: 'Blog configuration files installed.', ), new DirCopy( dir: 'dir/blog/views/', destDir: 'dir/to/store/views/blog/', description: 'Blog view files installed.', ), ); } /** * Return the actions to be processed on uninstall. * * @return ActionsInterface */ public function uninstall(): ActionsInterface { return new Actions( new FilesDelete( files: [ 'dir/to/store/config/' => [ 'blog.php', ], ], description: 'Blog configuration files uninstalled.', ), new DirDelete( dir: 'dir/to/store/views/blog/', description: 'Blog view files uninstalled.', ), ); } }
安装/卸载迁移
use Tobento\Service\Migration\Migrator; use Tobento\Service\Migration\AutowiringMigrationFactory; use Tobento\Service\Migration\MigrationJsonFileRepository; use Tobento\Service\Migration\MigrationInstallException; use Tobento\Service\Migration\MigrationUninstallException; use Tobento\Service\Container\Container; // Any PSR-11 container $container = new Container(); // Create migrator. $migrator = new Migrator( new AutowiringMigrationFactory($container), new MigrationJsonFileRepository(__DIR__.'/migrations/'), ); // Install a migration. try { $result = $migrator->install(\App\Blog\BlogMigration::class); } catch (MigrationInstallException $e) { // Handle exception. } // Uninstall a migration. if ($migrator->isInstalled(\App\Blog\BlogMigration::class)) { try { $result = $migrator->uninstall(\App\Blog\BlogMigration::class); } catch (MigrationUninstallException $e) { // Handle exception. } }
文档
迁移
创建迁移
您的迁移类必须实现MigrationInterface接口
use Tobento\Service\Migration\MigrationInterface; use Tobento\Service\Migration\ActionsInterface; use Tobento\Service\Migration\Actions; use Tobento\Service\Migration\Action\FilesCopy; use Tobento\Service\Migration\Action\FilesDelete; use Tobento\Service\Migration\Action\DirCopy; use Tobento\Service\Migration\Action\DirDelete; /** * BlogMigration */ class BlogMigration implements MigrationInterface { /** * Return a description of the migration. * * @return string */ public function description(): string { return 'Blog migration.'; } /** * Return the actions to be processed on install. * * @return ActionsInterface */ public function install(): ActionsInterface { return new Actions( new FilesCopy( files: [ 'dir/to/store/config/' => [ 'dir/blog/config/blog.php', ], ], description: 'Blog configuration files installed.', ), new DirCopy( dir: 'dir/blog/views/', destDir: 'dir/to/store/views/blog/', description: 'Blog view files installed.', ), ); } /** * Return the actions to be processed on uninstall. * * @return ActionsInterface */ public function uninstall(): ActionsInterface { return new Actions( new FilesDelete( files: [ 'dir/to/store/config/' => [ 'blog.php', ], ], description: 'Blog configuration files uninstalled.', ), new DirDelete( dir: 'dir/to/store/views/blog/', description: 'Blog view files uninstalled.', ), ); } }
操作
可调用
CallableAction::class
在处理过程中调用指定的可调用对象。
use Tobento\Service\Migration\Action\CallableAction; use Tobento\Service\Migration\ActionFailedException; $action = new CallableAction( callable: function ($name) { // do something on process }, // you may set parameters passed to the callable: parameters: ['name' => 'value'], name: 'A unique name', // or null description: 'Some description.', // (optional) type: 'keyword', // (optional) ); // Get the callable: var_dump(is_callable($action->getCallable())); // bool(true) // Get the parameters: var_dump($action->getParameters()); // array(1) { ["name"]=> string(5) "value" } var_dump($action->description()); // string(17) "Some description." var_dump($action->type()); // string(7) "keyword"
目录复制
使用DirCopy::class操作将目录复制到另一个目的地。
use Tobento\Service\Migration\Action\DirCopy; $action = new DirCopy( dir: 'dir/blog/views/', destDir: 'dir/to/store/views/blog/', overwrite: true, // if to overwrite existing dir (default true) name: 'A unique name', // or null description: 'Blog view files installed.', type: 'keyword', // (optional) ); var_dump($action->getDir()); // string(15) "dir/blog/views/" var_dump($action->getDestDir()); // string(24) "dir/to/store/views/blog/" var_dump($action->description()); // string(26) "Blog view files installed." var_dump($action->type()); // string(7) "keyword"
目录删除
使用DirDelete::class操作删除目录。
use Tobento\Service\Migration\Action\DirDelete; $action = new DirDelete( dir: 'dir/to/store/views/blog/', name: 'A unique name', // or null description: 'Blog view files uninstalled.', type: 'keyword', // (optional) ); var_dump($action->getDir()); // string(24) "dir/to/store/views/blog/" var_dump($action->description()); // string(28) "Blog view files uninstalled." var_dump($action->type()); // string(7) "keyword"
失败
Fail::class
在操作过程中总是失败,抛出ActionFailedException::class
,这在某些情况下可能很有用。
use Tobento\Service\Migration\Action\Fail; use Tobento\Service\Migration\ActionFailedException; $action = new Fail( failMessage: 'message', name: 'A unique name', // or null description: 'Some description.', // (optional) type: 'keyword', // (optional) ); $action->process(); // throws ActionFailedException with the specified fail message.
文件复制
使用FilesCopy::class操作将文件复制到另一个目录。
use Tobento\Service\Migration\Action\FilesCopy; $action = new FilesCopy( files: [ 'dir/to/store/config/' => [ 'dir/blog/config/blog.php', ], ], overwrite: true, // if to overwrite existing files (default true) name: 'A unique name', // or null description: 'Blog configuration files installed.', type: 'keyword', // (optional) ); var_dump($action->getFiles()); // array(1) { ["dir/to/store/config/"]=> array(1) { [0]=> string(24) "dir/blog/config/blog.php" } } // only available after processing the action. var_dump($action->getCopiedFiles()); // array(0) { } // only available after processing the action. var_dump($action->getSkippedFiles()); // array(0) { } var_dump($action->description()); // string(35) "Blog configuration files installed." var_dump($action->type()); // string(7) "keyword"
文件删除
使用FilesDelete::class操作删除文件。
use Tobento\Service\Migration\Action\FilesDelete; $action = new FilesDelete( files: [ 'dir/to/store/config/' => [ 'blog.php', ], ], name: 'A unique name', // or null description: 'Blog configuration files uninstalled.', type: 'keyword', // (optional) ); var_dump($action->getFiles()); // array(1) { ["dir/to/store/config/"]=> array(1) { [0]=> string(8) "blog.php" } } // only available after processing the action. var_dump($action->getDeletedFiles()); // array(0) { } var_dump($action->description()); // string(37) "Blog configuration files uninstalled." var_dump($action->type()); // string(7) "keyword"
文件字符串替换器
使用FileStringReplacer::class替换文件中的字符串。
use Tobento\Service\Migration\Action\FileStringReplacer; $action = new FileStringReplacer( file: 'dir/config/http.php', replace: [ '{key1}' => 'value1', '{key2}' => 'value2', ], name: 'A unique name', // or null description: 'Strings replaced.', type: 'keyword', // (optional) ); var_dump($action->getFile()); // string(19) "dir/config/http.php" var_dump($action->getReplace()); // array(2) { ["{key1}"]=> string(6) "value1" ["{key2}"]=> string(6) "value2" } var_dump($action->description()); // string(17) "Strings replaced." var_dump($action->type()); // string(7) "keyword"
空值
NullAction::class
根本不执行任何操作,这在某些情况下可能很有用。
use Tobento\Service\Migration\Action\NullAction; $action = new NullAction( name: 'A unique name', // or null description: 'Some description.', // (optional) type: 'keyword', // (optional) );
PDO执行
使用PdoExec::class执行pdo语句。
use Tobento\Service\Migration\Action\PdoExec; $action = new PdoExec( pdo: $pdo, statements: [ "CREATE TABLE IF NOT EXISTS blog ( id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT, active tinyint(1) UNSIGNED NOT NULL DEFAULT '0', PRIMARY KEY (id) ) ENGINE=InnoDB DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci AUTO_INCREMENT=0", ], name: 'A unique name', // or null description: 'Blog database tables installed.', type: 'keyword', // (optional) ); var_dump($action->getStatements()); // array var_dump($action->description()); // string(31) "Blog database tables installed." var_dump($action->type()); // string(7) "keyword"
自定义操作
以下是一个编写自定义操作的示例。
use Tobento\Service\Migration\ActionInterface; use Tobento\Service\Migration\ActionFailedException; class CustomAction implements ActionInterface { /** * Process the action. * * @return void * @throws ActionFailedException */ public function process(): void { // process the action } /** * Returns a name of the action. * * @return string */ public function name(): string { return $this::class; } /** * Returns a description of the action. * * @return string */ public function description(): string { return 'Action Description'; } /** * Returns the type of the action. * * @return string */ public function type(): string { return 'keyword'; } /** * Returns the processed data information. * * @return array<array-key, string> */ public function processedDataInfo(): array { return []; } }
处理操作
您可以在不使用迁移器的情况下处理操作。
use Tobento\Service\Migration\Action\DirCopy; use Tobento\Service\Migration\ActionFailedException; $action = new DirCopy( dir: 'dir/blog/views/', destDir: 'dir/to/store/views/blog/', description: 'Blog view files installed.', ); try { $action->process(); } catch (ActionFailedException $e) { // Handle exception. }
迁移器
创建迁移器
use Tobento\Service\Migration\Migrator; use Tobento\Service\Migration\MigratorInterface; use Tobento\Service\Migration\AutowiringMigrationFactory; use Tobento\Service\Migration\MigrationJsonFileRepository; use Tobento\Service\Container\Container; // Any PSR-11 container $container = new Container(); // Create migrator. $migrator = new Migrator( new AutowiringMigrationFactory($container), new MigrationJsonFileRepository('private/dir/migrations/'), ); var_dump($migrator instanceof MigratorInterface); // bool(true)
安装迁移
use Tobento\Service\Migration\MigrationResultInterface; use Tobento\Service\Migration\MigrationInstallException; try { $result = $migrator->install(AnyMigration::class); var_dump($result instanceof MigrationResultInterface); // bool(true) } catch (MigrationInstallException $e) { // Handle exception. }
如果您安装失败,您可能想卸载。
use Tobento\Service\Migration\MigrationResultInterface; use Tobento\Service\Migration\MigrationInstallException; use Tobento\Service\Migration\MigrationUninstallException; try { $result = $migrator->install(AnyMigration::class); } catch (MigrationInstallException $e) { try { $result = $migrator->uninstall(AnyMigration::class); } catch (MigrationUninstallException $e) { // Handle exception. } }
卸载迁移
use Tobento\Service\Migration\MigrationResultInterface; use Tobento\Service\Migration\MigrationUninstallException; try { $result = $migrator->uninstall(AnyMigration::class); var_dump($result instanceof MigrationResultInterface); // bool(true) } catch (MigrationUninstallException $e) { // Handle exception. }
您可能只想在迁移之前已安装的情况下卸载。
use Tobento\Service\Migration\MigrationResultInterface; use Tobento\Service\Migration\MigrationUninstallException; if ($migrator->isInstalled(\App\Blog\AnyMigration::class)) { try { $result = $migrator->uninstall(AnyMigration::class); } catch (MigrationUninstallException $e) { // Handle exception. } }
获取已安装的迁移
$migrations = $migrator->getInstalled(); // get only from specific. $migrations = $migrator->getInstalled(namespace: 'App\Blog\');
迁移结果
迁移器安装和卸载方法在成功时返回迁移结果对象
use Tobento\Service\Migration\MigrationResultInterface; use Tobento\Service\Migration\MigrationInterface; $result = $migrator->install(AnyMigration::class); var_dump($result instanceof MigrationResultInterface); // bool(true) $result = $migrator->uninstall(AnyMigration::class); var_dump($result instanceof MigrationResultInterface); // bool(true)
获取处理过的迁移
use Tobento\Service\Migration\MigrationInterface; // Get the migration processed: var_dump($result->migration() instanceof MigrationInterface); // bool(true)
获取处理过的迁移操作
use Tobento\Service\Migration\ActionsInterface; // Get the migration actions processed: var_dump($result->actions() instanceof ActionsInterface); // bool(true) // You might want to iterate over the actions. foreach($result->actions() as $action) { $description = $action->description(); }
迁移结果
您可能想将结果存储起来,以便稍后查看已处理的迁移。
use Tobento\Service\Migration\Migrator; use Tobento\Service\Migration\MigratorInterface; use Tobento\Service\Migration\AutowiringMigrationFactory; use Tobento\Service\Migration\MigrationJsonFileRepository; use Tobento\Service\Migration\MigrationInstallException; use Tobento\Service\Migration\MigrationResults; use Tobento\Service\Migration\MigrationResultsInterface; use Tobento\Service\Migration\MigrationResults; use Tobento\Service\Container\Container; // Any PSR-11 container $container = new Container(); // Create migrator. $migrator = new Migrator( new AutowiringMigrationFactory($container), new MigrationJsonFileRepository('private/dir/migrations/'), ); // MigrationResults implementation. $container->set(MigrationResultsInterface::class, function() { return new MigrationResults(); }); try { $result = $migrator->install(AnyMigration::class); // Add result. $container->get(MigrationResultsInterface::class)->add($result); } catch (MigrationInstallException $e) { // Handle exception. } // Somewhere later, do something with the results. $results = $container->get(MigrationResultsInterface::class)->all();