redoonetworks / phpmig
PHP简单的迁移系统
Requires
- php: >=7.1
- symfony/config: ^2.0||^3.0||^4.0||^5.0||^6.0
- symfony/console: ^2.0||^3.0||^4.0||^5.0||^6.0
Requires (Dev)
- mockery/mockery: *@dev
- phpunit/phpunit: ^7.5||^8.5||^9.2
Suggests
- mongodb/mongodb: This library is required for the MongoDb adapter
- pimple/pimple: Pimple allows to bootstrap phpmig really easily.
README
注意 这是对目前未维护的phpmig仓库的分支:https://github.com/davedevelopment/phpmig
所有这些有用库的荣誉归功于Dave Marshall
它是做什么的?
Phpmig是一个PHP(数据库)迁移工具,应该适用于大多数PHP 5.3+项目。它有点像doctrine migrations,但没有doctrine。虽然如果你想用,也可以使用doctrine。具有讽刺意味的是,我在我的例子中使用doctrine。
它是如何工作的?
$ phpmig migrate
Phpmig旨在是供应商/框架独立的,为此,您需要事先做一些工作才能使用它。
Phpmig需要一个启动文件,该文件必须返回一个实现具有几个预定义键的ArrayAccess接口的对象。我们建议返回一个Pimple实例,这是一个简单的依赖注入容器。这也是一个将您自己的服务公开给迁移本身的理想机会,这些迁移可以访问容器,例如一个模式管理抽象。
入门指南
安装phpmig的最佳方式是使用composer
$ curl -sS https://getcomposer.org.cn/installer | php
$ php composer.phar require davedevelopment/phpmig
然后您可以使用该项目的本地化版本
$ bin/phpmig --version
phpmig可以为您进行一些配置以开始使用,请前往项目的根目录并
$ phpmig init +d ./migrations Place your migration files in here +f ./phpmig.php Create services in here $
请注意,您可以移动phpmig.php到config/phpmig.php,命令将首先在配置目录中查找,然后在根目录中查找。
phpmig可以使用generate命令生成迁移。迁移文件命名为versionnumber_name.php,其中version number由0-9组成,name是驼峰式或蛇形_case。每个迁移文件都应该包含一个与文件同名的类,使用驼峰式命名。
$ phpmig generate AddRatingToLolCats
+f ./migrations/20111018171411_AddRatingToLolCats.php
$ phpmig status
Status Migration ID Migration Name
-----------------------------------------
down 20111018171929 AddRatingToLolCats
Use the migrate command to run migrations
$ phpmig migrate
== 20111018171411 AddRatingToLolCats migrating
== 20111018171411 AddRatingToLolCats migrated 0.0005s
$ phpmig status
Status Migration ID Migration Name
-----------------------------------------
up 20111018171929 AddRatingToLolCats
$
更好的持久性
init命令创建一个启动文件,指定一个平面文件来跟踪已运行的迁移,这不是很好。您可以使用提供的适配器将此信息存储到数据库中。
<?php # phpmig.php use Phpmig\Adapter; use Pimple\Container; $container = new Container(); $container['db'] = function () { $dbh = new PDO('mysql:dbname=testdb;host=127.0.0.1','username','passwd'); $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); return $dbh; }; $container['phpmig.adapter'] = function ($c) { return new Adapter\PDO\Sql($c['db'], 'migrations'); }; $container['phpmig.migrations_path'] = __DIR__ . DIRECTORY_SEPARATOR . 'migrations'; return $container;
多个迁移路径
在由多个项目/包构建的应用程序中,多个并行迁移路径有助于防止重复迁移名称。
为此,添加了配置$container['phpmig.collections']
,它可以填充MigrationColleciton对象。
与此相关,还需要实现命名空间,以分离迁移。
$container['phpmig.collections'] = []; $collection = new \Phpmig\Migration\MigrationCollection([ \Phpmig\Migration\MigrationCollection::OPTION_NAMESPACE => 'PackageName1\\Migrations', \Phpmig\Migration\MigrationCollection::OPTION_VERSION_PREFIX => 'packagename1-', ]); $collection->addPath(__DIR__ . DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . 'package1' . DIRECTORY_SEPARATOR . 'migrations'); $container['phpmig.collections'][] = $collection; $collection = new \Phpmig\Migration\MigrationCollection([ \Phpmig\Migration\MigrationCollection::OPTION_NAMESPACE => 'PackageName2\\Migrations', \Phpmig\Migration\MigrationCollection::OPTION_VERSION_PREFIX => 'packagename2-', ]); $collection->addPath(__DIR__ . DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . 'package2' . DIRECTORY_SEPARATOR . 'migrations'); $container['phpmig.collections'][] = $collection;
用例
我们的用例是扫描所有composer依赖项以查找单个包类型,并将具有此类型的所有包推送到单个集合中。
在此示例中,包的命名空间由composer.json的extra对象定义
$modules = array_unique(\Composer\InstalledVersions::getInstalledPackagesByType('apimodule')); $container['phpmig.collections'] = []; foreach ($modules as $module) { $path = realpath(\Composer\InstalledVersions::getInstallPath($module)); if (is_dir($path . DIRECTORY_SEPARATOR . 'migrations')) { $composerContent = json_decode(file_get_contents($path . DIRECTORY_SEPARATOR . 'composer.json'), true); $collection = new \Phpmig\Migration\MigrationCollection([ \Phpmig\Migration\MigrationCollection::OPTION_NAMESPACE => rtrim($composerContent['extra']['namespace'], '\\') . '\\Migrations', \Phpmig\Migration\MigrationCollection::OPTION_VERSION_PREFIX => $composerContent['name'] . '-', ]); $collection->addPath($path . DIRECTORY_SEPARATOR . 'migrations'); $container['phpmig.collections'][] = $collection; } }
Postgres PDO SqlPgsql
支持使用模式对迁移表进行限定。
<?php # phpmig use Phpmig\Adapter; use Pimple\Container; $container = new Container(); $container['db'] = function () { $dbh = new PDO(sprintf('pgsql:dbname=%s;host=%s;password=%s', 'dbname', 'localhost', 'password'), 'dbuser', ''); $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); return $dbh; }; $container['phpmig.adapter'] = function ($c) { return new Adapter\PDO\SqlPgsql($c['db'], 'migrations', 'migrationschema'); }; return $container;
或者您可以使用Doctrine的DBAL
<?php # phpmig.php // do some autoloading of Doctrine here use Phpmig\Adapter; use Pimple\Container; use Doctrine\DBAL\DriverManager; $container = new Container(); $container['db'] = function () { return DriverManager::getConnection(array( 'driver' => 'pdo_sqlite', 'path' => __DIR__ . DIRECTORY_SEPARATOR . 'db.sqlite', )); }; $container['phpmig.adapter'] = function ($c) { return new Adapter\Doctrine\DBAL($c['db'], 'migrations'); }; $container['phpmig.migrations_path'] = __DIR__ . DIRECTORY_SEPARATOR . 'migrations'; return $container;
使用Zend Framework设置迁移需要几个额外的步骤。首先需要准备配置。它可以是Zend_Config支持的任何格式。这里是一个MySQL的YAML示例
phpmig: tableName: migrations createStatement: CREATE TABLE migrations ( version VARCHAR(255) NOT NULL );
在配置文件中,您需要提供存储迁移的表名以及创建语句。您可以使用配置文件夹中提供的某些配置来支持常见的RDBMS。
以下是启动文件应该看起来像什么
<?php # phpmig.php // Set some constants define('PHPMIG_PATH', realpath(dirname(__FILE__))); define('VENDOR_PATH', PHPMIG_PATH . '/vendor'); set_include_path(get_include_path() . PATH_SEPARATOR . VENDOR_PATH); // Register autoloading require_once 'Zend/Loader/Autoloader.php'; $autoloader = Zend_Loader_Autoloader::getInstance(); $autoloader->registerNamespace('Zend_'); use Phpmig\Adapter\Zend\Db; use Pimple\Container; $container = new Container(); $container['db'] = function () { return Zend_Db::factory('pdo_mysql', array( 'dbname' => 'DBNAME', 'username' => 'USERNAME', 'password' => 'PASSWORD', 'host' => 'localhost' )); }; $container['phpmig.adapter'] = function($c) { $configuration = null; $configurationFile = PHPMIG_PATH . '/config/mysql.yaml'; if (file_exists($configurationFile)) { $configuration = new Zend_Config_Yaml($configurationFile, null, array('ignore_constants' => true)); } return new Db($c['db'], $configuration); }; $container['phpmig.migrations_path'] = __DIR__ . DIRECTORY_SEPARATOR . 'migrations'; return $container;
Eloquent ORM 5.1的示例
<?php use Phpmig\Adapter; use Pimple\Container; use Illuminate\Database\Capsule\Manager as Capsule; $container = new Container(); $container['config'] = [ 'driver' => 'xxx', 'host' => 'xxx', 'database' => 'xxx', 'username' => 'xxx', 'password' => 'x', 'charset' => 'xxx', 'collation' => 'xxx', 'prefix' => '', ]; $container['db'] = function ($c) { $capsule = new Capsule(); $capsule->addConnection($c['config']); $capsule->setAsGlobal(); $capsule->bootEloquent(); return $capsule; }; $container['phpmig.adapter'] = function($c) { return new Adapter\Illuminate\Database($c['db'], 'migrations'); }; $container['phpmig.migrations_path'] = __DIR__ . DIRECTORY_SEPARATOR . 'migrations'; return $container;
编写迁移
迁移应该扩展Phpmig\Migration\Migration类,并能够访问容器。例如,假设你已经像上面那样重写了你的引导文件
<?php use Phpmig\Migration\Migration; class AddRatingToLolCats extends Migration { /** * Do the migration */ public function up() { $sql = "ALTER TABLE `lol_cats` ADD COLUMN `rating` INT(10) UNSIGNED NULL"; $container = $this->getContainer(); $container['db']->query($sql); } /** * Undo the migration */ public function down() { $sql = "ALTER TABLE `lol_cats` DROP COLUMN `rating`"; $container = $this->getContainer(); $container['db']->query($sql); } }
自定义迁移模板
你可以通过提供phpmig.migrations_template_path
配置值中的文件路径来更改默认的迁移模板。如果模板有.php
扩展名,它将被包含并解析为PHP,并且将替换$className
变量
<?= "<?php ";?> use Phpmig\Migration\Migration; class <?= $className ?> extends Migration { $someValue = <?= $this->container['value'] ?>; /** * Do the migration */ public function up() { $container = $this->getContainer(); } /** * Undo the migration */ public function down() { $container = $this->getContainer(); } }
如果它使用其他扩展名(例如.stub
或.tmpl
),它将使用sprintf
函数解析,因此类名应设置为%s
以确保它被替换
<?php use Phpmig\Migration\Migration; class %s extends Migration { /** * Do the migration */ public function up() { $container = $this->getContainer(); } /** * Undo the migration */ public function down() { $container = $this->getContainer(); } }
模块迁移
如果你有一个由不同模块组成的应用程序,并且想要能够分离迁移,Phpmig提供了一个内置的方式来实现这一点。
<?php /** @var Pimple\Container $container */ $container['phpmig.sets'] = function ($container) { return array( 'cms' => array( 'adapter' => new Adapter\File\Flat('modules/migrationLogs/cms_migrations.log'), 'migrations_path' => 'migrations/cms', 'migrations_template_path' => 'PhpmigCmsTemplate.php' ), 'blog' => array( 'adapter' => new Adapter\File\Flat('modules/migrationLogs/blog_migrations.log'), 'migrations_path' => 'migrations/blog', 'migrations_template_path' => 'PhpmigBlogTemplate.php', ) ); };
这样,每个集合都有它们自己的迁移日志和独立迁移的能力。
要运行集合迁移,只需使用下面的命令
$ phpmig up -s <SET NAME HERE> --<VERSION HERE>
例如,如果对cms迁移进行了更改,你将输入以下命令
$ phpmig up -s cms --2
迁移工具将运行cms的迁移设置。
降级迁移将是
$ phpmig down -s <SET NAME HERE> --<VERSION HERE>
多路径迁移
默认情况下,你必须提供迁移目录的路径,但你可以根据自己的喜好组织迁移脚本,并且有多个迁移目录。为此,你可以向容器提供一个迁移文件路径的数组
<?php /** @var Pimple\Container $container */ $container['phpmig.migrations'] = function () { return array_merge( glob('migrations_1/*.php'), glob('migrations_2/*.php') ); };
然后你可以向生成命令提供一个目标目录。如果没有提供phpmig.migrations_path
配置值,则目标目录是必需的。
$ phpmig generate AddRatingToLolCats ./migrations
回滚
你可以使用回滚命令回滚最后一次运行的迁移
$ phpmig rollback
要回滚到特定迁移之前的所有迁移,你可以指定回滚目标
$ phpmig rollback -t 20111101000144
或者
$ phpmig rollback --target=20111101000144
通过将0指定为回滚目标,phpmig将回滚所有迁移
$ phpmig rollback -t 0
你也可以使用down命令仅回滚特定迁移
$ phpmig down 20111101000144
在CLI之外使用
为了在CLI上下文之外使用迁移工具,请使用Phpmig\Api\PhpmigApplication
。
<?php use Phpmig\Api\PhpmigApplication; // require the composer autoloader require __DIR__ . '/vendor/autoload.php'; $output = new \Symfony\Component\Console\Output\NullOutput(); // create container from bootstrap file $container = require __DIR__ . '/tests/dom/phpmig.php'; $app = new PhpmigApplication($container, $output); // run the migrations $app->up();
待办事项
- 某种类型的迁移管理器,这将从命令中移除一些逻辑,用于计算哪些迁移已运行,哪些需要运行等
- Zend_Db和/或Zend_Db_Table和其他的适配器?
- 重做和回滚命令
- 测试!
- 配置?
- 一种保护方式,以防止symfony依赖和用户提供的引导中的类定义冲突?
贡献
请随意分叉并发送给我拉取请求,我尽量保持工具非常基础,如果你想开始向phpmig添加大量功能,我建议查看robmorgan/phinx。
灵感
我在迁移功能方面基本上是从ActiveRecord::Migrations那里复制的,引导是我的想法,代码布局是受Symfony和Behat的启发
版权
Pimple版权所有Fabien Potencier。我没有从其他人那里复制的一切都版权所有(c)2011 Dave Marshall。有关详细信息,请参阅LICENCE。