g4 / ruckusing-migrations
生成和管理数据库迁移的框架
Requires
- php: >=5.2.0
Requires (Dev)
- phpunit/phpunit: 3.7.*
README
Ruckusing 是一个用 PHP5 编写的框架,用于生成和管理一组“数据库迁移”。数据库迁移是声明性文件,代表了数据库(其表、列、索引等)在特定时间点的状态。通过使用数据库迁移,多个开发者可以同时工作在同一个应用上,并保证应用在所有远程开发者机器上保持一致状态。
该框架的灵感来源于 Ruby on Rails 内置的迁移系统。任何熟悉 RoR 迁移的人都会立刻感到熟悉。
入门 & 文档
有关支持的迁移方法和如何入门的完整文档,请参阅 Wiki。
支持的数据库
- Postgres
- MySQL
- Sqlite
特性
-
便携性:描述要创建的表、列、索引等的迁移文件是用纯 PHP5 编写的,然后在运行时转换为相应的 SQL。这允许透明地支持任何 RDBMS 的单个迁移文件集(假设有适配器,见下文)。
-
类似 "rake" 的基本任务支持。框架有一个“任务”的概念(实际上框架的主要焦点,迁移,只是一个简单的任务),它只是实现了接口的 PHP5 类。任务可以自由编写,只要它们遵循特定的命名约定并实现特定的接口,框架就会自动注册它们并允许它们执行。
-
能够向上或向下到特定的迁移状态。
-
生成骨架迁移文件的代码生成器。
-
支持基于模块的迁移目录,可以从指定的模块目录生成/运行迁移文件。
-
开箱即用的基本任务支持,如初始化数据库模式信息表(db:setup)、请求当前版本(db:version)和转储当前模式(db:schema)。
限制
- PHP 5.2+ 是硬性要求。该框架广泛使用了 PHP5 的面向对象特性。没有计划使框架向后兼容。
配置
- 将
/path/to/ruckusing-migrations/config/database.inc.php
复制到/path/to/mycodebase/ruckusing.conf.php
,并更新development
键以使用您的数据库凭据
type
是 pgsql
、mysql
、sqlite
之一,取决于您的数据库,以及 migrations_dir
、db_dir
、log_dir
、ruckusing_base
路径。
-
如果您想使用模块迁移目录,编辑
/path/to/mycodebase/ruckusing.conf.php
并更新migrations_dir
如array('default' => '/default/path', 'module_name' => '/module/migration/path')
路径。 -
将
/path/to/ruckusing-migrations/ruckus.php
复制到/path/to/mycodebase/ruckus.php
。
自定义任务
lib/Task
中的所有任务默认启用。如果您想实现自定义任务,则可以在您覆盖的 ruckusing.conf.php
中的 tasks_dir
键中指定任务目录
# ruckusing.conf.php return array( /* ... snip ... */, 'tasks_dir' => RUCKUSING_WORKING_BASE . '/custom_tasks' );
生成骨架迁移文件
从代码库的顶层运行
$ php ruckus.php db:generate create_users_table
Created OK
Created migration: 20121112163653_CreateUsersTable.php
模块迁移目录示例
$ php ruckus.php db:generate create_items_table module=module_name
Created OK
Created migration: 20121112163653_CreateItemsTable.php
生成的文件位于 migrations
目录下。打开该文件,您会看到它看起来像
class CreateUsersTable extends Ruckusing_Migration_Base {
public function up() {
}//up()
public function down() {
}//down()
}
以下所有方法都应在 up()
和 down()
方法中实现。
环境
您可以通过 ENV
命令行参数切换环境。默认环境是 development
。
要指定额外的环境,将其添加到 ruckusing.conf.php
中的 db
键下。
以不同环境运行
$ ENV=test php db:migrate
运行迁移
运行所有挂起的迁移
$ php ruckus.php db:migrate
回滚最近一次的迁移
$ php ruckus.php db:migrate VERSION=-1
回滚到特定的迁移(指定要回滚到迁移的文件名中的时间戳)
$ php ruckus.php db:migrate VERSION=20121114001742
可用的迁移方法概述
可用的方法如下(以下为简要列表,详细用法请见下文)
数据库级操作
create_database
drop_database
表级操作
create_table
drop_table
rename_table
列级操作
add_column
remove_column
rename_column
change_column
add_timestamps
索引级操作
add_index
remove_index
查询执行
execute
select_one
select_all
数据库级操作
有两种数据库级操作,分别是 create_database
和 drop_database
。在此级别上操作数据库的迁移很少使用。
创建新数据库
此命令有点无用,因为通常您会运行迁移以针对现有数据库(使用传统的 RDMBS 创建方法创建和设置)。但是,如果您想从迁移创建另一个数据库,此方法可用。
方法调用:create_database
参数 name
:新数据库的名称
示例
$this->create_database("my_project");
删除数据库
要完全删除数据库及其所有表(及其数据)。
方法调用:drop_database
参数 name
:现有数据库的名称
示例
$this->drop_database("my_project");
此方法可能是所有方法中最复杂的,但也是最广泛使用的方法之一。
方法调用:create_table
参数
name
:新表的名称
options
:创建新表的可选选项数组(可选)。
支持的选项键/值对如下
id
:布尔值 - 框架是否自动生成主键。对于 MySQL,该列将命名为 id
并为整数类型,具有自增属性。
options
:表示传递给创建表命令末尾的最终化参数的字符串。通常,这用于指定 MySQL 的存储引擎,例如 'options' => 'Engine=InnoDB'
假设 最终此方法委托给适当的 RDMBS 适配器,MySQL 适配器对表的结构有一些重要的假设。
表级操作
数据库迁移框架提供了丰富的创建、删除和重命名表的功能。
创建表
调用 $this->create_table(...)
实际上返回一个 TableDefinition
对象。框架的此方法是非常少数实际上返回结果,您必须与之交互的方法之一(作为最终用户)。
创建新表的步骤如下
- 使用名称和任何可选选项创建表,并存储返回值以供以后使用
$users = $this->create_table("users");
- 向表定义中添加列
$users->column("first_name", "string"); $users->column("last_name", "string");
- 调用
finish()
以使用定义及其列实际创建表
$users->finish();
默认情况下,表类型将是数据库的默认类型。要指定不同的表类型(例如 InnoDB),将 options
键传递到 $options
数组中,例如
示例 A: 创建一个名为 users
的新 InnoDB 表。
$this->create_table('users', array('options' => 'Engine=InnoDB'));
- 此命令还假定您希望有一个
id
列。此列 不需要 明确指定,它将自动生成,除非通过$options
数组中的id
键明确指示不要生成。
示例 B: 创建一个名为 users
的新表,但不要自动创建主键。
$this->create_table('users', array('id' => false));
主键列将使用属性 int(11) unsigned auto_increment
创建。
示例 C: 指定自己的主键名为 'guid'
$t = $this->create_table('users', array('id' => false, 'options' => 'Engine=InnoDB')); $t->column('guid', 'string', array('primary_key' => true, 'limit' => 64)); $t->finish();
删除表
可以通过使用 drop_table
方法调用来删除表。正如预期的那样,删除表也会删除其所有列和任何索引。
方法调用: drop_table
参数: table_name
: 要删除的表名。
示例
$this->drop_table("users");
重命名表
可以使用 rename_table
方法重命名表。
方法调用: rename_table
参数: table_name
: 已存在的表名。 new_name
: 表的新名称。
示例
// rename from "users" to "people" $this->rename_table("users", "people");
列级操作
向表中添加新列
有关添加新列的完整文档,请参阅 添加列
删除列
删除数据库列非常简单,但请注意,与该列关联的任何索引也将被删除。
方法调用: remove_column
参数: table_name
: 从中删除列的表名。
column_name
: 要删除的列。
示例 A: 从 users
表中删除 age
列。
$this->remove_column("users", "age");
重命名列
可以重命名数据库列(假设底层 RDMBS/适配器支持它)。
方法调用: rename_column
参数: table_name
: 从中重命名列的表名。
column_name
: 列的现有名称。
new_column_name
: 列的新名称。
示例 A: 将 users
表中的 first_name
重命名为 fname
$this->rename_column("users", "first_name", "fname");
修改现有列
可以修改现有列的类型、默认值或 NULL
支持。如果您只想重命名列,则使用 rename_column
方法。此方法接受列类型的通用类型,以及影响列定义的选项数组。有关可用类型和选项,请参阅添加新列的文档,AddingColumns。
方法调用: change_column
参数: table_name
: 要修改列的表名。
column_name
: 要更改的列名。
type
: 列所需的通用类型。
options
: (可选)影响列定义的选项关联数组。
示例 A: 从 users
表中更改 first_name
列的长度为 128。
$this->change_column("users", "first_name", "string", array('limit' => 128) );
添加时间戳列
我们经常需要列来标记 创建时间 和 更新时间 操作。这个方便的方法可以轻松为您生成它们。
方法调用:add_timestamps
参数: table_name
: 要添加列的表名
created_name
: 创建时间 列的名称,默认为 created_at
updated_name
: 更新时间 列的名称,默认为 updated_at
示例 A: 向 users
表添加时间戳列。
$this->add_timestamps("users");
示例 B: 向 users
表添加时间戳列,列名为 created
和 updated
。
$this->add_timestamps("users", "created", "updated");
索引级操作
可以使用框架方法创建和删除索引。
添加新索引
方法调用: add_index
参数: table
: 要添加索引的表名。
column
:要创建索引的列。如果这是一个字符串,则假定它是列的名称,索引将是一个单列索引。如果是数组,则假定它是列名称的列表,那么索引将是一个多列索引,在指定的列上。
options
:(可选) 一组选项的关联数组,用于控制索引生成。键 / 值对
unique
:值:true
或 false
。是否为该列创建唯一索引。默认为 false
。
name
:值:用户定义。索引的名称。如果没有指定,将根据表和列名生成默认名称。
已知问题/解决方案:MySQL 当前将标识符名称限制为 64 个字符。当使用 add_index 且未指定索引名称时,Ruckusing 将根据表名和被索引的列(s)生成一个合适名称。例如,如果有 users 表,并且正在对 username 列生成索引,则生成的索引名称将是:idx_users_username 。如果尝试添加一个多列索引,则生成的名称可能超过 MySQL 的 64 个字符限制。在这种情况下,Ruckusing 将引发错误,建议您通过 name 选项参数使用自定义索引名称。请参阅 示例 C。
示例 A:在 users
表的 email
列上创建索引。
$this->add_index("users", "email");
示例 B:在 users
表的 ssn
列上创建唯一索引。
$this->add_index("users", "ssn", array('unique' => true)));
示例 C:在 posts
表的 blog_id
列上创建索引,但指定索引的特定名称。
$this->add_index("posts", "blog_id", array('name' => 'index_on_blog_id'));
示例 D:在 users
表的 email
和 ssn
列上创建多列索引。
$this->add_index("users", array('email', 'ssn') );
删除索引
很简单。如果索引是使用此方法(add_index
)的兄弟方法创建的,则只需要指定该方法(但调用 remove_index
)的相同参数。
方法调用: remove_index
参数: table_name
:要从其中删除索引的表名称。
column_name
:要从其中删除索引的列名称。
options
:(可选) 一组选项的关联数组,用于控制索引删除过程。键 / 值对:name
:值:用户定义。要删除的索引的名称。如果没有指定,将根据表和列名生成默认名称。如果在索引创建过程中(使用 add_index
方法)指定了 name
,则在此处也需要这样做,并指定相同的名称。否则,生成的默认名称可能与实际的索引名称不匹配。
示例 A:从 users
表的 email
列中删除(单列)索引。
$this->remove_index("users", "email");
示例 B:从 users
表的 email
和 ssn
列中删除(多列)索引。
$this->remove_index("users", array("email", "ssn") );
示例 C:从 users
表的 email
列中删除(单列)命名的索引。
$this->remove_index("users", "email", array('name' => "index_on_email_column") );
查询执行
通过一组方法提供任意查询执行。
执行方法
execute()
方法旨在用于不返回任何数据的查询,例如 INSERT
、UPDATE
或 DELETE
。
示例 A:根据某些条件更新所有行
$this->execute("UPDATE foo SET name = 'bar' WHERE .... ");
返回结果的查询
对于返回结果的查询,例如 SELECT
查询,则使用 select_one
或 select_all
,具体取决于您要返回的内容。
这两个方法都返回一个关联数组,数组的每个元素本身又是另一个关联数组,包含列名称及其值。
select_one()
用于期望单个结果集的查询,而 select_all()
适用于所有其他情况(可能不知道将获得多少行)。
注意:由于这些方法接受原始 SQL 查询作为输入,它们可能不一定可以在所有关系型数据库管理系统(RDBMS)之间移植。
示例 A (select_one
): 获取列的总和
$result = $this->select_one("SELECT SUM(total_price) AS total_price FROM orders"); if($result) { echo "Your revenue is: " . $result['total_price']; }
**示例 B (select_all
) **: 获取所有行并遍历每一行,执行某些操作
$result = $this->select_all("SELECT email, first_name, last_name FROM users WHERE created_at >= SUBDATE( NOW(), INTERVAL 7 DAY)"); if($result) { echo "New customers: (" . count($result) . ")\n"; foreach($result as $row) { printf("(%s) %s %s\n", $row['email'], $row['first_name'], $row['last_name']); } }
测试
单元测试需要安装 phpunit: http://www.phpunit.de/manual/current/en/installation.html
运行完整测试套件
$ vi config/database.inc.php
$ mysql -uroot -p < tests/test.sql
$ psql -Upostgres -f tests/test.sql
$ phpunit
将在 tests/unit
中运行所有测试类。
运行单个测试文件
$ vi config/database.inc.php
$ mysql -uroot -p < tests/test.sql
$ phpunit tests/unit/MySQLAdapterTest.php
某些测试需要定义 mysql_test
或 pg_test
数据库配置。如果需要但未满足,则测试将相应地报错。