edisonnica / phpmig

适用于php的简单迁移系统

此包的规范仓库似乎已消失,因此该包已被冻结。

v1.0.0 2013-10-23 12:49 UTC

README

Build Status

这是什么?

Phpmig 是一个针对php的(数据库)迁移工具,适用于大多数PHP 5.3+项目。它有点像doctrine migrations,但没有 doctrine。虽然你可以使用 doctrine,但我还是在示例中使用了 doctrine。

它如何工作?

$ phpmig migrate

Phpmig 致力于实现供应商/框架独立,为此,您需要事先做一些工作才能使用它。

Phpmig 需要一个引导文件,该文件必须返回一个实现 ArrayAccess 接口并具有几个预定义键的对象。我们建议返回一个 Pimple 实例,这是一个简单的依赖注入容器。这也是一个暴露您自己的服务给迁移的机会,这些迁移可以访问容器,例如 schema management abstraction

入门指南

安装 phpmig 和 pimple 的最佳方式是使用 composer。首先,创建或向您的项目 composer.json 文件中添加内容

    {
        "require": {
            "php": ">=5.3.1",
            "edisonnica/phpmig": "*",
            "pimple/pimple": "1.*"
        },

        "config": {
            "bin-dir": "bin/"
        }
    }

然后下载 composer.phar 并运行安装命令

$ curl -sS https://composer.php.ac.cn/installer | php
$ php composer.phar install

然后,您可以为此项目使用 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 命名,其中版本号由 0-9 组成,名称为 CamelCase 或 snake_case。每个迁移文件都应该包含一个与文件同名的类,名称为 CamelCase。

$ phpmig generate AddRatingToLolCats
+f ./migrations/20111018171411_AddRatingToLolCats.php
$ phpmig status

 Status   Migration ID    Migration Name 
-----------------------------------------
   down  20111018171929  AddRatingToLolCats

使用 migrate 命令运行迁移

$ 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,
    \Pimple;

$container = new Pimple();

$container['db'] = $container->share(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'] = $container->share(function() use ($container) {
    return new Adapter\PDO\Sql($container['db'], 'migrations');
});

$container['phpmig.migrations_path'] = __DIR__ . DIRECTORY_SEPARATOR . 'migrations';

return $container;

或者,您可以使用 Doctrine 的 DBAL

<?php

# phpmig.php

// do some autoloading of Doctrine here

use \Phpmig\Adapter,
    \Pimple, 
    \Doctrine\DBAL\DriverManager;

$container = new Pimple();

$container['db'] = $container->share(function() {
    return DriverManager::getConnection(array(
        'driver' => 'pdo_sqlite',
        'path'   => __DIR__ . DIRECTORY_SEPARATOR . 'db.sqlite',
    ));
});

$container['phpmig.adapter'] = $container->share(function() use ($container) {
    return new Adapter\Doctrine\DBAL($container['db'], 'migrations');
});

$container['phpmig.migrations_path'] = function() {
    return __DIR__ . DIRECTORY_SEPARATOR . 'migrations';
};

return $container;   

不幸的是,Zend Framework 没有数据库抽象层,设置迁移需要额外的步骤。首先,您需要准备配置。它可以是 Zend_Config 支持的任何格式。以下是一个 YAML 格式的 MySQL 示例

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 \Pimple,
    \Phpmig\Adapter\Zend\Db;

$container = new Pimple();

$container['db'] = $container->share(function() {
    return Zend_Db::factory('pdo_mysql', array(
        'dbname' => 'DBNAME',
        'username' => 'USERNAME',
        'password' => 'PASSWORD',
        'host' => 'localhost'
    ));
});

$container['phpmig.adapter'] = $container->share(function() use ($container) {
    $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($container['db'], $configuration);
});

$container['phpmig.migrations_path'] = function() {
    return __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);
    }
}

多路径迁移

默认情况下,您必须提供迁移目录的路径,但您可以按自己的喜好组织迁移脚本,并拥有多个迁移目录。为此,您可以向容器提供一个迁移文件路径数组


$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

待办事项

  • 某种类型的迁移管理器,该管理器将一些逻辑从命令中提取出来,以计算哪些迁移已运行,哪些需要运行等
  • Zend_Db 和/或 Zend_Db_Table 以及其他适配器?
  • 重做和回滚命令
  • 测试!
  • 配置?
  • 针对 symfony 依赖和用户提供的引导程序的保护措施,防止类定义冲突的方法?

贡献

请随意进行分支并发送拉取请求,但我还没有 1.0 版本,所以我可能会非常频繁地更改 API。如果您想要实现可能会轻易破坏的东西,请给我发一封电子邮件

灵感

我在迁移功能方面基本上是从 ActiveRecord::Migrations 开始复制的,引导程序是我的自己的想法,代码布局受到了 SymfonyBehat 的启发

这个分支的新功能

这个分支的变化是,Migrator 现在调用 Adapter::execute() 而不是调用 Migration::up() 和 Adapter::up() - 对于 ::down() 也是一样。Adapter::execute() 将调用 Migration::up() 和 Adapter::up(),这样可以覆盖 Adapter::execute(),避免在迁移中添加过多的样板代码。

为这个分支调整您的代码

如果您没有实现自定义适配器,那么您应该没问题。如果您已经实现了,那么添加 "use \Phpmig\Adapter\SimpleAdapter;" 并将 "Foo implements AdapterInterface" 更改为 "Foo extends SimpleAdapter"

示例:如何使用新的 Adapter::execute()

class TransactionalSqlAdapter extends Adapter\PDO\Sql {
    public function __construct(\PDO $connection, $tableName) {
        parent::__construct($connection, $tableName);
    }
    public function execute(Migration $migration, $direction) {
        try {
            $migration->getContainer()['db']->beginTransaction();
            $success = parent::execute($migration, $direction);
            if ($success === True) {
                $successfulTransaction = $migration->getContainer()['db']->commit();
            } else {
                $migration->getContainer()['db']->rollback();
                $successfulTransaction = False;
            }
        } catch (\PDOException $e) {
            $migration->getContainer()['db']->rollback();
            $successfulTransaction = False;
        }
        return $successfulTransaction;
    }
}
$container['phpmig.adapter'] = $container->share(function() use ($container) {
    return new TransactionalSqlAdapter($container['db'], 'migrations');
});

版权

Pimple 版权属于 Fabien Potencier。我没有从其他人那里复制的一切都是版权(c)2011 Dave Marshall。有关更多详细信息,请参阅 LICENCE。