注意:数据库迁移特性从
1.1.6
版本可用。
像源代码一样,随着我们开发和维护数据库驱动的应用程序的同时,数据库的结构也正在发展。例如,在开发过程中,我们可能需要添加一个新表;或在应用程序投入生产后,我们可能会意识到需要在列上添加索引。跟我们的源代码一样,跟踪这些结构化数据库更改(称为迁移)非常重要。如果源代码和数据库不同步,则整个系统很可能会中断。因此,Yii 提供了一个数据库迁移工具,可以跟踪数据库迁移历史记录,应用新的迁移或还原现有的迁移历史记录。
以下步骤显示如何在开发过程中使用数据库迁移:
- Tim 创建了一个新的迁移(例如创建一个新表)
- Tim 承诺新的迁移到源代码控制系统(如SVN,GIT)
- Doug 从源代码管理系统更新并接收新的迁移
- Doug 将迁移应用到他的本地开发数据库
Yii 通过 yiic migrate
命令行工具支持数据库迁移。此工具支持创建新的迁移,应用/恢复/重做迁移,以及显示迁移历史记录和新的迁移。
在下面,我们将介绍如何使用这个工具。
注意:最好使用特定应用的
migrate
命令(例如cd path/to/protected
)而不是框架目录中的那个。 确保你有可写的protected\migrations
目录。 还要检查你是否在protected/config/console.php
中配置了数据库连接。
1. 创建迁移
要创建新的迁移(例如创建新闻表),我们运行以下命令:
yiic migrate create <name>
必需的 name
参数用于指定迁移的简单描述(例如 create_news_table
)。 正如我们将在下面显示的,name
参数将作为 PHP 类名称的一部分。 因此,它应只包含字母,数字和或下划线字符。
yiic migrate create create_news_table
上述命令将在 protected/migrations
目录下创建一个名为 m101129_185401_create_news_table.php
的新文件,其中包含以下初始代码:
class m101129_185401_create_news_table extends CDbMigration
{
public function up()
{
}
public function down()
{
echo "m101129_185401_create_news_table does not support migration down.\n";
return false;
}
/*
// implement safeUp/safeDown instead if transaction is needed
public function safeUp()
{
}
public function safeDown()
{
}
*/
}
请注意,类名和文件名相同。文件名格式 m<timestamp>_<name>
中的 <timestamp>
是指迁移创建时的 UTC 时间戳(格式为 yymmdd_hhmmss
),而 <name>
取自命令的 name
参数。
up()
方法应该包含实现实际数据库迁移的代码,而 down()
方法可能包含恢复 up()
中所做的修改的代码。 有时,实现 down()
是不可能的。 例如,如果我们在 up()
方法中删除了表行,那么我们将无法在 down()
中恢复它们。 在这种情况下,迁移称为不可逆转,这意味着我们无法回滚到数据库的先前状态。 在上面生成的代码中,down()
方法返回 false
,表示迁移不能被还原。
提示:从
1.1.7
版本开始,如果up()
或者down()
方法返回了false
,后续的所有迁移将被取消。在1.1.6
版本之前,则必须通过抛出异常来取消后续迁移。
例如,我们来看看有关创建新闻表的迁移。
class m101129_185401_create_news_table extends CDbMigration
{
public function up()
{
$this->createTable('tbl_news', array(
'id' => 'pk',
'title' => 'string NOT NULL',
'content' => 'text',
));
}
public function down()
{
$this->dropTable('tbl_news');
}
}
CDbMigration
基类提供了一组用于操纵数据库的数据和模式的方法。 例如,CDbMigration::createTable
将创建一个数据库表,而 CDbMigration::insert
将插入一行数据。 这些方法都使用 CDbMigration::getDbConnection()
返回的数据库连接,默认情况下返回 Yii::app()->db
。
提示:你可能注意到
CDbMigration
提供的数据库方法与CDbCommand
非常相似。是的,它们几乎相同,只是CDbMigration
的方法会测量方法执行的时间,并打印一些与方法参数有关的消息。
2. 事务迁移
提示:事务迁移从
1.1.7
版本可用。
在执行复杂的数据库迁移时,我们通常希望确保每个迁移都成功或失败,使数据库保持一致性和完整性。 为了实现这一目标,我们可以利用数据库事务。
我们可以明确地启动一个数据库事务,并将与数据库相关的代码包含在事务中,如下所示:
class m101129_185401_create_news_table extends CDbMigration
{
public function up()
{
$transaction=$this->getDbConnection()->beginTransaction();
try
{
$this->createTable('tbl_news', array(
'id' => 'pk',
'title' => 'string NOT NULL',
'content' => 'text',
));
$transaction->commit();
}
catch(Exception $e)
{
echo "Exception: ".$e->getMessage()."\n";
$transaction->rollback();
return false;
}
}
// ...similar code for down()
}
然而,获得事务支持的一个简单方法是实现 safeUp()
和 safeDown()
方法,而不是实现 up()
和 down()
方法。
class m101129_185401_create_news_table extends CDbMigration
{
public function safeUp()
{
$this->createTable('tbl_news', array(
'id' => 'pk',
'title' => 'string NOT NULL',
'content' => 'text',
));
}
public function safeDown()
{
$this->dropTable('tbl_news');
}
}
当 Yii 执行迁移时,它会启动一个数据库事务,然后调用 safeUp()
或者 safeDown()
。如果在 safeUp()
或者 safeDown()
中出现任意的数据库错误,事务将会被回滚,这就能确保数据库保持良好的状态。
提示:并非所有的 DBMS(Database Management System,数据库管理系统)都支持事务,而且有些数据库查询也不能放到事务中。这种情况下,您将不得不实现
up()
和down()
, 对于 MySQL 和 MariaDB,一些 SQL 语句可能会导致隐式提交(有关详细信息,请参阅 MySQL 和 MariaDB 的文档)。
3. 应用迁移
要应用所有可用的新迁移(即使本地数据库更新),请运行以下命令:
yiic migrate
该命令将显示所有新迁移的列表。如果您确认应用迁移,它将按照类名中的时间戳值的顺序,在每个新的迁移类中依次运行 up() 方法。
应用迁移后,迁移工具将在名为 tbl_migration
的数据库表中保留记录。这允许工具识别已应用哪些迁移,哪些不适用。如果 tbl_migration
表不存在,该工具将自动在数据库应用程序组件指定的数据库中创建它。
有时,我们可能只想应用一个或几个新的迁移。我们可以使用以下命令:
yiic migrate 3
此命令将应用 3 个新的迁移。更改值 3
将允许我们更改要应用的迁移数。
我们还可以使用以下命令将数据库迁移到特定版本:
yiic migrate to 101129_185401
也就是说,我们使用迁移名称的时间戳部分来指定我们要将数据库迁移到的版本。如果在最后一次应用的迁移和指定的迁移之间存在多个迁移,将应用所有这些迁移。如果之前已经应用了指定的迁移,那么应用之后的所有迁移将被恢复(在下一节中将对此进行描述)。
4. 恢复迁移
要还原最后一个或多个已应用的迁移,我们可以使用以下命令:
yiic migrate down [step]
其中可选步骤参数指定要还原的迁移数量。 它默认为 1,表示还原上次应用的迁移。
如前所述,并非所有迁移都可以恢复。 尝试恢复这样的迁移会抛出异常并停止整个还原过程。
5. 重做迁移
重做迁移意味着首先恢复,然后应用指定的迁移。 这可以通过以下命令完成:
yiic migrate redo [step]
其中可选步骤参数指定要重做的迁移数量。 它默认为 1,意味着重做最后一次迁移。
6. 显示迁移信息
除了应用和还原迁移之外,迁移工具还可以显示要应用的迁移历史记录和新的迁移。
yiic migrage history [limit]
yiic migrate new [limit]
其中可选参数限制指定要显示的迁移数。如果未指定 limit
,将显示所有可用的迁移。
第一个命令显示已应用的迁移,而第二个命令显示未应用的迁移。
7. 修改迁移历史
有时,我们可能希望将迁移历史记录修改为特定的迁移版本,而不实际应用或还原相关的迁移。这通常发生在开发新的迁移时。我们可以使用以下命令来实现这个目标。
yiic migrate mark 101129_185401
此命令非常类似于 yiic
迁移到命令,除了它仅将迁移历史表修改为指定版本,而不应用或还原迁移。
8. 自定义迁移命令
有几种方法可以自定义迁移命令。
使用命令行选项
迁移命令带有四个可在命令行中指定的选项:
interactive
:boolean
,指定是否在交互模式下执行迁移。默认为true
,表示执行特定迁移时将提示用户。如果迁移在后台进程中完成,您可能将其设置为false
。migrationPath
:string
,指定存储所有迁移类文件的目录。必须使用路径别名来指定,并且相应的目录必须存在。如果没有指定,它将使用应用程序基础路径下的迁移子目录。migrationTable
:string
,指定用于存储迁移历史信息的数据库表的名称。它默认为tbl_migration
。表结构是version varchar(255)primary key,apply_time int
。connectionID
:string
,指定数据库应用程序组件的 ID。默认为'db'
。templateFile
:string
,指定要作为生成迁移类的代码模板的文件的路径。这必须使用路径别名(例如application.migrations.template
)来指定。如果未设置,将使用内部模板。在模板中,令牌{ClassName}
将替换为实际的迁移类名称。 要指定这些选项,请使用以下格式执行migrate
命令
yiic migrate up --option1=value1 --option2=value2 ...
例如,如果我们要迁移一个论坛模块的迁移文件位于模块的迁移目录中,我们可以使用以下命令:
yiic migrate up --migrationPath=ext.forum.migrations
请注意,如果您设置布尔选项,例如使用交互式命令行,则应像下面这样赋值 1
或 0
:
yiic migrate --interactive=0
全局配置命令
虽然命令行选项允许我们即时配置迁移命令,但有时我们可能希望为所有配置命令一次。 例如,我们可能希望使用不同的表来存储迁移历史记录,或者我们可能需要使用自定义的迁移模板。 我们可以通过修改控制台应用程序的配置文件,如下所示:
return array(
......
'commandMap'=>array(
'migrate'=>array(
'class'=>'system.cli.commands.MigrateCommand',
'migrationPath'=>'application.migrations',
'migrationTable'=>'tbl_migration',
'connectionID'=>'db',
'templateFile'=>'application.migrations.template',
),
......
),
......
);
现在,如果我们运行 migrate
命令,上述配置将生效,而不需要我们每次都输入命令行选项。