注意,数据迁移这玩意1.1.6以后才可以用哦。另外,说一下,英文是migrate,翻译为迁移,但是我看大部分是数据库的变更操作而已啊。

就跟源代码一样的,数据库结构在我们开发维护数据库驱动应用的同时,也在发展着。例如,在我们开发的时候,我们会想新创建一个数据库的表,或者是在开发完成,项目上线后,才发现要给某个字段加一个索引。很重要的一点,必须记录跟踪这些数据库结构方面的变化(迁移)。如果源代码或者是数据库不一致了,整个系统都很有可能被破坏了。出于这个原因,Yii提供了一个数据库迁移工具,可以记录数据库的变化记录,应用新的架构,或者是回滚到之前的架构。

以下步骤演示了我们在开发时如何使用数据迁移的工具:

1. 张三创建了一个新的变更(例如,创建了张表)

2. 张三把这个变更提交到了SVN。

3. 李四从SVN获得更新后的结构。

4. 李四把这些更新应用到他自己本地的数据库。

Yii通过调用yiic migrate的命令行工具来实现数据迁移的。这个工具支持新建数据库迁移,应用,回滚,重做。可以查看迁移记录,以及查看最新的镜像。

接下来,我们将介绍怎么使用这个工具。

注意:最好是用工程下的yiic,而不要用框架下的yiic命令。确保你的protected\migrations存在,并且目录可写。同时,还要检查你的数据库连接protected/config/console.php。

1.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的文件,里面的初始代码为:

 
 
 
  
  1. class m101129_185401_create_news_table extends CDbMigration  
  2. {  
  3.     public function up()  
  4.     {  
  5.     }  
  6.     public function down()  
  7.     {  
  8.         echo "m101129_185401_create_news_table does not support migration down.nn";  
  9.         return false;  
  10.     }  
  11.     /*  
  12.     // implement safeUp/safeDown instead if transaction is needed  
  13.     public function safeUp()  
  14.     {  
  15.     }  
  16.     public function safeDown()  
  17.     {  
  18.     }  
  19.     */ 

注意到,上面类名跟文件是同名的,命名规则为m<timestamp>_<name>。<timestamp>是UTC的时间戳(格式为yymmdd_hhmmss)。

up()方法里面包含了这次迁移所要做的实际动作;down()方法里,包含了回滚需要做的操作。

有些时候,是没有办法做回滚的。例如,我们在up时删除了一张数据表,我们在down的时候就没有办法恢复了。在这种情况下,迁移就被称之为不可逆的,意味着我们无法回滚到数据库之前的那个状态中。在上面的例子中,down()方法返回false,表示无法回滚。

INFO:从1.1.7版本开始,如果up或者down的方法返回false,所有的接下来的操作都会被取消。在1.1.6版本中,必须抛出异常,然后再取消接下来的迁移工作。

下面的例子,让我们来看看新建一张表:

 
  
  1. class m101129_185401_create_news_table extends CDbMigration  
  2. {  
  3.     public function up()  
  4.     {  
  5.         $this->createTable('tbl news'array(  
  6.             'id' => 'pk',  
  7.             'title' => 'string NOT NULL',  
  8.             'content' => 'text',  
  9.         ));  
  10.     }  
  11.     public function down()  
  12.     {  
  13.         $this->dropTable('tbl news');  
  14.     }  

基类CDbMigration提供了一些方法,用来管理数据库的数据以及结构。例如,CDbMigration::createTable用来创建新表,CDbMigration::insert用来插入记录。这些方法都是用CDbMigration::getDbConnection()返回的句柄,默认是Yii::app()->db。

INFO:你会注意到,CDbMigration很像CDbCommand的命令。事实如此。

1.2 数据库变迁事务

INFO:次功能是从yii的1.1.7 版本开始才支持

当执行复杂的数据库变迁时,我们希望每个变动都是同时成功或者失败,这样可以保证数据库的一致性。要做到这点,就用我们之前提到过的数据库事务。

我们在开始的时候使用事务,然后在所有动作之后,结束事务:

 
  
  1. class m101129_185401_create_news_table extends CDbMigration  
  2. {  
  3.     public function up()  
  4.     {  
  5.         $transaction=$this->getDbConnection()->beginTransaction();  
  6.         try  
  7.         {  
  8.             $this->createTable('tbl news'array(  
  9.                 'id' => 'pk',  
  10.                 'title' => 'string NOT NULL',  
  11.                 'content' => 'text',  
  12.             ));  
  13.             $transaction->commit();  
  14.         }  
  15.         catch(Exception $e)  
  16.         {  
  17.             echo "Exception: ".$e->getMessage()."nn";  
  18.             $transaction->rollBack();  
  19.             return false;  
  20.         }  
  21.     }  
  22.     // ...similar code for down()  

或者是,采用更加简便的方法,用safeUp(),safeDown() 替换up()以及down()两个方法:

 
  
  1. class m101129_185401_create_news_table extends CDbMigration  
  2. {  
  3.     public function safeUp()  
  4.     {  
  5.         $this->createTable('tbl news'array(  
  6.             'id' => 'pk',  
  7.             'title' => 'string NOT NULL',  
  8.             'content' => 'text',  
  9.         ));  
  10.     }  
  11.     public function safeDown()  
  12.     {  
  13.         $this->dropTable('tbl news');  
  14.     }  

当Yii执行变迁时,会先开始一个数据库事务,然后再调用safeUp()的方法(或者是safeDown())。如果在这两个方法中有任何的错误,事务就会回滚,保证数据库的完整性。

注意:并不是所有的数据库系统都支持事务操作,另外,还有一些操作不能用在事务中。这种情况下,就要用up()跟down()了。在MYSQL当中,有些语句会被隐式提交的。

1.3 应用变迁

如果想应用所有的新变更,执行以下的语句:

yiic migrate

这个指令会列出所有新的变迁,如果你确定应用这些变迁,就会调用这些变迁类当中的up()方法,按照时间顺序一个一个的执行。

当一个变迁应用以后,变迁工具就会在数据库中的一个名为tbl_migration表中,生成一条记录。这条记录用来帮助变迁工具辨别哪些变迁已经应用了,哪些还没有。如果tbl_migration的表不存在,变迁工具会自动在数据库创建一张。

有时候,我们只想更新一个或者是部分的变迁,我们可以用这个命令:

yiic migrate up 3

这个命令会应用3个新的变迁,当然我们可以更改3这个数字来决定应用几个。

当然,我们也可以指定应用到哪个新变迁为止:

yiic migrate to 101129_185401

上面方法中,我们用时间戳来指定我们想应用的变迁。如果从上次应用的变迁,到这个指定的变迁,中间还有许多没应用的,所有这些都会被应用。如果这个指定的变迁是被应用过的,那么这个之后的所有应用过的变迁都会被回滚。

1.4 回滚应用变迁

如果要回滚上一次或者是很多次之前的变迁,用以下的命令:

yiic migrate down [step]

step 代表想回滚应用的变迁数量,默认值是1, 也就是回滚到上一次的应用变迁前。

正如我们之前提到过的,并不是所有被应用过的变迁都可以回滚。如果尝试回滚这样的变迁,整个回滚都会终止。

1.5 重做变迁

重做,也就是之前应用过,回滚过,现在又要重新应用。可以按照以下的命令:

yiic migrate redo [step]

step的参数跟上面回滚是一样的。

1.6 查询变迁信息

除了应用,回滚数据库变迁,这个变迁工具还可以用来查询应用的变迁记录信息。

yiic migrate history [limit]
yiic migrate new [limit]

参数limit指定了查询的个数,如果没指定该参数值,所有的变迁记录都会被查询出来。

第一个命令列出被应用过的变迁,第二个列出没被应用过的。

1.7 修改变迁记录

有时候,我们想修改这个变迁记录的版本,但是又不真的去应用或者回滚该变迁。这经常发生于我们开发新的变迁时,我们可以用以下的命令来实现:

yiic migrate mark 101129_185401

这个指令跟yiic migrate非常相似,区别在于这个命令只是修改了数据库中记录的变迁记录,实际上并没有应用或者回滚该操作。

1.8 自定义变迁指令

有许多方式可以自定义变迁指令。

用命令行选项

命令行中,有4个选项可以用来自定义:

●interactive:boolean型。用来指定应用变迁时,是否采用互动模式。默认值是true,也就是在应用时,会提醒用户。如果是false,那么就是在后台执行。

●migrationPath:string型。指定变迁类文件的存放路径。这个路径必须用别名的方式,而且必须存在。如果不存在,则会用工程下的migrations子目录。

●migrationTable:string型。指定数据库中的变迁记录表。默认值是tbl_migration。表结构是version varchar(255) primary key, apply time integer。

●connectionID:string型。指定数据库连接句柄,默认是db。

●templateFile:string型。指定生成变迁类时的代码模板路径。同样的,需要路径别名的格式。如果不存在,则内置的模板会被使用。

要指定这些选项,调用变迁指令时,用以下的格式:

yiic migrate up --option1=value1 --option2=value2 ...

例如,我们要从forum模块下的变迁文件来应用,可以用下面的代码:

yiic migrate up --migrationPath=ext.forum.migrations

设置全局命令

虽然命令行的选项让我们可以在用的时候你可以有选择,但是有时候我们想一次性就配置好。例如,我们想用另外的一张表来保存变迁记录,或者我们想用一个自定义的变迁模板,我们可以在工程的控制面板进行工程:

 
  
  1. return array(  
  2.     ......  
  3.     'commandMap'=>array(  
  4.         'migrate'=>array(  
  5.             'class'=>'system.cli.commands.MigrateCommand',  
  6.             'migrationPath'=>'application.migrations',  
  7.             'migrationTable'=>'tbl migration',  
  8.             'connectionID'=>'db',  
  9.             'templateFile'=>'application.migrations.template',  
  10.         ),  
  11.     ......  
  12.     ),  
  13.     ......  
  14. ); 

这时候,当我们调用migrate的命令行时,上面的选项配置就会生效了。