阿江 2017-10-02 08:37:22 5740次浏览 0条回复 2 3 0

说明

学习Yii Framework 2易2框架的过程是漫长的也是充满乐趣的以下是我学习Yii2框架时对官网英文资料(请参见原文网址)的翻译和代码实现提供了较完整的代码供你参考不妥之处请多多指正

原文网址:

http://www.yiiframework.com/doc-2.0/guide-db-migrations.html
http://www.yiiframework.com/doc-2.0/guide-db-migrations.html#creating-migrations
1、Creating Migrations(创建迁移)
http://www.yiiframework.com/doc-2.0/guide-db-migrations.html#generating-migrations
2、Generating Migrations(生成迁移)

本文主题:为数据库创建迁移和生成迁移(migration)

在开发和维护数据库驱动的应用过程中,数据库的结构与源代码的开发同步更新。例如,在应用开发的过程中,新建了一张表,在应用部署到生产环境后,发现需要为这张表创建一个索引以提升查询性能,等等。因为数据库结构改变后需要源代码随之而改变,Yii支持此类数据库迁移特征,这样你就可以用数据库迁移的形式追踪数据库的变化,也就是与源代码同步的版本控制。

以下步骤展示了在团队开发过程中如何使用数据库迁移: 1、Tim创建了一个新的迁移(例如,创建了一张新表,更改了一个字段定义等) 2、Tim提交了新迁移到源代码控制系统中(如Git,Mercurial) 3、Doug从源码控制系统中更新了他的仓库,并接收了新的迁移 4、Doug应用新迁移到他本地的开发数据库,也就是同步了Tim所做的数据库变更到他自己的数据库中。

以下步骤展示了如何部署一个新的数据库迁移版本到生产环境: 1、Scott为项目仓库的新数据库迁移创建了一个版本标签。 2、Scott在生产服务器上更新源码到新版本标签。 3、Scott应用所累积的数据库迁移到生产数据库中。

Yii提供了一组迁移命令工具让你可以: 1、创建新迁移 2、应用迁移 3、恢复迁移 4、再次应用迁移 5、显示迁移历史和状态

所有这些工具都可以通过命令yii migrate来使用,本章节我们将描述使用这些工具完成各项任务的细节,你可以使用帮助命令yii help migrate获取每个工具的用法。

小贴士:迁移不仅影响到数据库的schema,还将调整原有数据以适应新迁移,创建RBAC分层结构或者清除缓存。

1、创建迁移(Creating Migrations)

//table//migrate 要创建一个新的迁移,运行以下命令: yii migrate/create 必填参数name对新迁移进行了概述,例如,如果本次迁移是创建了一个新表news,你可以使用名称create_news_table,并运行以下命令:

D:\phpwork\basic>yii migrate/create create_news_table
Yii Migration Tool (based on Yii v2.0.8)
Create new migration 'D:\phpwork\basic/migrations\m170313_082453_create_news_tab
le.php'? (yes|no) [no]:y
New migration created successfully.
D:\phpwork\basic>

注意:因为name参数将被用于创建迁移类的名称,所以它只能包含字母,数字和下划线。 以上命令将会在@app/migrations目录下创建一个新的名为m150101_185401_create_news_table.php的PHP类文件。此文件主要是定义了一个迁移类m150101_185401_create_news_table框架代码,代码如下:

<?php
use yii\db\Migration;
class m150101_185401_create_news_table extends Migration
{
    public function up()
    {

    }
    public function down()
    {
        echo "m101129_185401_create_news_table cannot be reverted.\n";

        return false;
    }
    /*
    // Use safeUp/safeDown to run migration code within a transaction
    public function safeUp()
    {
    }

    public function safeDown()
    {
    }
    */
}

每一个数据库迁移被定义为一个继承自yii\db\Migration的PHP类,这个迁移类名称是自动生成的,其格式为:m_,这里: ,指迁移创建命令运行时的UTC时间 ,是你在运行命令时提供的name参数值

在迁移类中,你需要在up()方法中编写代码,以明确在数据库结构上做出哪些改变。你可能也想要在down()方法中编写代码,以恢复那些被up()做过的修改。当你做此迁移升级数据库时,up()方法将被调用;当需要数据库降回去时,将调用down()方法。以下代码展示了,创建news表时,你需要去完成的迁移类:

use yii\db\Schema;
use yii\db\Migration;

class m150101_185401_create_news_table extends Migration
{
    public function up()
    {
        $this->createTable('news', [
            'id' => Schema::TYPE_PK,
            'title' => Schema::TYPE_STRING . ' NOT NULL',
            'content' => Schema::TYPE_TEXT,
        ]);
    }

    public function down()
    {
        $this->dropTable('news');
    }
}

信息:不是所有的迁移都可以恢复的。例如,如果在up()方法中删除了表中的一行记录,你可能无法在down()方法中恢复此行记录。有时,你可以不必太关注实现down()方法,因为通常并不需要恢复数据库迁移。在这种情况下,你可以在down()方法中返回false,以明确告知此迁移不支持恢复。

基础迁移类yii\db\Migration定义了一个使用db属性的数据库连接,你可以使用它操作数据库shcema,这些方法在Working with Database Schema章节中已描述: http://www.yiiframework.com/doc-2.0/guide-db-dao.html#database-schema

创建一个表或列时,与使用物理类型相比,更好的方式是使用抽象类型,这样你的迁移不会依赖于特定的DBMS。yii\db\Schema类定义了一组常量来表示其所支持的抽象类型。这些常量格式为TYPE_。例如,TYPE_PK指自增长的主键类型;TYPE_STRING指字符串类型。当一个迁移应用到一个特定的数据库时,抽象类型会被转换为对应的物理类型。当是MySQL时,TYPE_PK将被转换为int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,而TYPE_STRING对应varchar(255)。

当使用抽象类型时,你可以追加额外的常量,NOT NULL被追加到Schema::TYPE_STRING以定义此列不能为空(null)。

信息:抽象类型与物理类型之间的对应关系在每个具体的QueryBuilder类中由$typeMap 属性来定义。 例如:mysql的$typeMap 定义: D:\phpwork\basic\vendor\yiisoft\yii2\db\mysql\QueryBuilder.php

自2.0.6版起,你可以使用新引入的结构构建器,它提供了定义列结构更为简便的方法。上例所示的迁移就可以改写为:

<?php
use yii\db\Migration;
class m150101_185401_create_news_table extends Migration
{
    public function up()
    {
        $this->createTable('news', [
            'id' => $this->primaryKey(),
            'title' => $this->string()->notNull(),
            'content' => $this->text(),
        ]);
    }
    public function down()
    {
        $this->dropTable('news');
    }
}

定义列类型所有的有效方法可以在API文档中查看: http://www.yiiframework.com/doc-2.0/yii-db-schemabuildertrait.html

2、生成迁移(Generating Migrations)

自2.0.7版本开始,迁移控制台提供更加方便的方法去创建迁移。 如果迁移名称是特定形式,例如create_xxx_table或drop_xxx_table,那么生成的迁移文件将包含额外的代码,也就是创建或删除表。接下来将描述此特征的各种变化:

//Create Table(创建表) yii migrate/create create_post_table //实际使用的是“yii migrate/create create_post”,如下:

D:\phpwork\basic>yii migrate/create create_post
Yii Migration Tool (based on Yii v2.0.8)
Create new migration 'D:\phpwork\basic/migrations\m170314_032934_create_post.php
'? (yes|no) [no]:y
New migration created successfully.
D:\phpwork\basic>

//将生成代码:

/**
 * Handles the creation for table `post`.
 */
class m150811_220037_create_post extends Migration
{
    /**
     * @inheritdoc
     */
    public function up()
    {
        $this->createTable('post', [
            'id' => $this->primaryKey()
        ]);
    }
    /**
     * @inheritdoc
     */
    public function down()
    {
        $this->dropTable('post');
    }
}

通过--fields选项可以直接定义表的字段:

D:\phpwork\basic>yii migrate/create create_post --fields="title:string,body:text"
Yii Migration Tool (based on Yii v2.0.8)
Create new migration 'D:\phpwork\basic/migrations\m170314_033234_create_post.php'? (yes|no) [no]:y
New migration created successfully.
D:\phpwork\basic>

//生成的代码: D:\phpwork\basic\migrations\m170314_033234_create_post.php

use yii\db\Migration;
/**
 * Handles the creation for table `post`.
 */
class m170314_033234_create_post extends Migration
{
    /**
     * @inheritdoc
     */
    public function up()
    {
        $this->createTable('post', [
            'id' => $this->primaryKey(),
            'title' => $this->string(),
            'body' => $this->text(),
        ]);
    }
    /**
     * @inheritdoc
     */
    public function down()
    {
        $this->dropTable('post');
    }
}

//也可以创建更多的字段属性

D:\phpwork\basic>yii migrate/create create_post --fields="title:string(12):notNUll:unique,body:text"
Yii Migration Tool (based on Yii v2.0.8)
Create new migration 'D:\phpwork\basic/migrations\m170314_033848_create_post.php'? (yes|no) [no]:y
New migration created successfully.
D:\phpwork\basic>

//将生成 D:\phpwork\basic\migrations\m170314_033848_create_post.php

use yii\db\Migration;
/**
 * Handles the creation for table `post`.
 */
class m170314_033848_create_post extends Migration
{
    /**
     * @inheritdoc
     */
    public function up()
    {
        $this->createTable('post', [
            'id' => $this->primaryKey(),
            'title' => $this->string(12)->notNUll()->unique(),
            'body' => $this->text(),
        ]);
    }
    /**
     * @inheritdoc
     */
    public function down()
    {
        $this->dropTable('post');
    }
}

注意:主键是自动被加上的,并默认命名为id,如果想要使用其他名字,你可以定义为 --fields="name:primaryKey"

//Foreign keys(外键) 从2.0.8开始,生成器支持使用foreignKey生成外键:

D:\phpwork\basic>yii migrate/create create_post --fields="author_id:integer:notNull:foreignKey(user),category_id:integer:defaultValue(1):foreignKey,title:string(12):notNUll:unique,body:text"
Yii Migration Tool (based on Yii v2.0.8)
Create new migration 'D:\phpwork\basic/migrations\m170314_035136_create_post.php'? (yes|no) [no]:y
New migration created successfully.
D:\phpwork\basic>

//将生成: D:\phpwork\basic\migrations\m170314_035136_create_post.php

use yii\db\Migration;
/**
 * Handles the creation for table `post`.
 * Has foreign keys to the tables:
 *
 * - `user`
 * - `category`
 */
class m170314_035136_create_post extends Migration
{
    /**
     * @inheritdoc
     */
    public function up()
    {
        $this->createTable('post', [
            'id' => $this->primaryKey(),
            'author_id' => $this->integer()->notNull(),
            'category_id' => $this->integer()->defaultValue(1),
            'title' => $this->string(12)->notNUll()->unique(),
            'body' => $this->text(),
        ]);
        // creates index for column `author_id`
        $this->createIndex(
            'idx-post-author_id',
            'post',
            'author_id'
        );
        // add foreign key for table `user`
        $this->addForeignKey(
            'fk-post-author_id',
            'post',
            'author_id',
            'user',
            'id',
            'CASCADE'
        );
        // creates index for column `category_id`
        $this->createIndex(
            'idx-post-category_id',
            'post',
            'category_id'
        );
        // add foreign key for table `category`
        $this->addForeignKey(
            'fk-post-category_id',
            'post',
            'category_id',
            'category',
            'id',
            'CASCADE'
        );
    }
    /**
     * @inheritdoc
     */
    public function down()
    {
        // drops foreign key for table `user`
        $this->dropForeignKey(
            'fk-post-author_id',
            'post'
        );
        // drops index for column `author_id`
        $this->dropIndex(
            'idx-post-author_id',
            'post'
        );
        // drops foreign key for table `category`
        $this->dropForeignKey(
            'fk-post-category_id',
            'post'
        );
        // drops index for column `category_id`
        $this->dropIndex(
            'idx-post-category_id',
            'post'
        );
        $this->dropTable('post');
    }
}

在列描述中foreignKey关键字的位置并不会改变生成的代码,它的意思是: author_id:integer:notNull:foreignKey(user) author_id:integer:foreignKey(user):notNull author_id:foreighKey(user):integer:notNull 将会产生同样的代码。

foreignKey关键字可以在括号中带一个参数,此参数是要生成的外键所关联的表名称,如果没有传递参数,将从列名中推演出表名称。

在上例中auth_id:integer:notNull:foreignKey(user)将生成名为author_id的字段,此字段有一个user表的外键;category_id:integer:defaultValue(1):foreignKey将生成名为category_id的字段,此字段有一个category的外键。 从2.0.11开始,foreignKey关键字可以接收第2个参数,使用空格分隔,它可以指定生成的外键所关联的列名称,如果第2参数被忽略,列名称将从表结构(table schema)中获取,如果表结构没有存在,主键没有设置或是复合主键,将使用默认名称:id。

//Drop Table(删除表)

D:\phpwork\basic>yii migrate/create drop_post --fields="title:string(12):noNull:unique,body:text"
Yii Migration Tool (based on Yii v2.0.8)
Create new migration 'D:\phpwork\basic/migrations\m170314_054507_drop_post.php'? (yes|no) [no]:y
New migration created successfully.
D:\phpwork\basic>

//将生成: D:\phpwork\basic\migrations\m170314_054507_drop_post.php

use yii\db\Migration;
/**
 * Handles the dropping for table `post`.
 */
class m170314_054507_drop_post extends Migration
{
    /**
     * @inheritdoc
     */
    public function up()
    {
        $this->dropTable('post');
    }
    /**
     * @inheritdoc
     */
    public function down()
    {
        $this->createTable('post', [
            'id' => $this->primaryKey(),
            'title' => $this->string(12)->noNull()->unique(),
            'body' => $this->text(),
        ]);
    }
}

//Add Column(添加列)//column 如果迁移名称样式是add_xxx_column_to_yyy,将在生成的文件中包含addColumn和dropColumn语句。

D:\phpwork\basic>yii migrate/create add_position_column_to_post --fields="position:integer"
Yii Migration Tool (based on Yii v2.0.8)
Create new migration 'D:\phpwork\basic/migrations\m170314_054954_add_position_column_to_post.php'? (yes|no) [no]:y
New migration created successfully.
D:\phpwork\basic>

//将生成: D:\phpwork\basic\migrations\m170314_054954_add_position_column_to_post.php

use yii\db\Migration;
/**
 * Handles adding position_column to table `post`.
 */
class m170314_054954_add_position_column_to_post extends Migration
{
    /**
     * @inheritdoc
     */
    public function up()
    {
        $this->addColumn('post', 'position', $this->integer());
    }
    /**
     * @inheritdoc
     */
    public function down()
    {
        $this->dropColumn('post', 'position');
    }
}

//一次添加多个字段:

D:\phpwork\basic>yii migrate/create add_position_column_to_post --fields="position:integer,click:integer"

//Drop Column(删除列) 如果迁移名称样式是drop_xxx_column_from_yyy,生成的文件将包含dropColumn和addColumn语句。

D:\phpwork\basic>yii migrate/create drop_position_column_from_post --fields="position:integer"
Yii Migration Tool (based on Yii v2.0.8)
Create new migration 'D:\phpwork\basic/migrations\m170314_060321_drop_position_column_from_post.php'? (yes|no) [no]:y
New migration created successfully.
D:\phpwork\basic>

//将生成: D:\phpwork\basic\migrations\m170314_060321_drop_position_column_from_post.php

use yii\db\Migration;
/**
 * Handles dropping position_column from table `post`.
 */
class m170314_060321_drop_position_column_from_post extends Migration
{
    /**
     * @inheritdoc
     */
    public function up()
    {
        $this->dropColumn('post', 'position');
    }
    /**
     * @inheritdoc
     */
    public function down()
    {
        $this->addColumn('post', 'position', $this->integer());
    }
}

//Add Junction Table(添加Junction表) 如果迁移名称样式是create_junction_table_for_xxx_and_yyy_tables或create_junction_xxx_and_yyy_tables,那么创建junction表的代码将会被生成。

D:\phpwork\basic>yii migrate/create create_junction_for_post_and_tag --fields="created_at:dateTime"
Yii Migration Tool (based on Yii v2.0.8)
Create new migration 'D:\phpwork\basic/migrations\m170314_060957_create_junction_for_post_and_tag.php'? (yes|no) [no]:y
New migration created successfully.
D:\phpwork\basic>

//将生成: D:\phpwork\basic\migrations\m170314_060957_create_junction_for_post_and_tag.php

use yii\db\Migration;
/**
 * Handles the creation for table `for_post_tag`.
 * Has foreign keys to the tables:
 *
 * - `for_post`
 * - `tag`
 */
class m170314_060957_create_junction_for_post_and_tag extends Migration
{
    /**
     * @inheritdoc
     */
    public function up()
    {
        $this->createTable('for_post_tag', [
            'for_post_id' => $this->integer(),
            'tag_id' => $this->integer(),
            'created_at' => $this->dateTime(),
            'PRIMARY KEY(for_post_id, tag_id)',
        ]);
        // creates index for column `for_post_id`
        $this->createIndex(
            'idx-for_post_tag-for_post_id',
            'for_post_tag',
            'for_post_id'
        );
        // add foreign key for table `for_post`
        $this->addForeignKey(
            'fk-for_post_tag-for_post_id',
            'for_post_tag',
            'for_post_id',
            'for_post',
            'id',
            'CASCADE'
        );
        // creates index for column `tag_id`
        $this->createIndex(
            'idx-for_post_tag-tag_id',
            'for_post_tag',
            'tag_id'
        );
        // add foreign key for table `tag`
        $this->addForeignKey(
            'fk-for_post_tag-tag_id',
            'for_post_tag',
            'tag_id',
            'tag',
            'id',
            'CASCADE'
        );
    }
    /**
     * @inheritdoc
     */
    public function down()
    {
        // drops foreign key for table `for_post`
        $this->dropForeignKey(
            'fk-for_post_tag-for_post_id',
            'for_post_tag'
        );
        // drops index for column `for_post_id`
        $this->dropIndex(
            'idx-for_post_tag-for_post_id',
            'for_post_tag'
        );
        // drops foreign key for table `tag`
        $this->dropForeignKey(
            'fk-for_post_tag-tag_id',
            'for_post_tag'
        );
        // drops index for column `tag_id`
        $this->dropIndex(
            'idx-for_post_tag-tag_id',
            'for_post_tag'
        );
        $this->dropTable('for_post_tag');
    }
}

自2.0.11版本开始,Junction表的外键列名称从表结构中获取,如果结构中没有定义表,或主键没有设置或是复合主键,将使用默认值:id。

//Transactional Migrations(事务化迁移) 当执行复杂的DB迁移时,确保每次迁移是整体成功或失败是非常重要的,这样使得数据库能够保持完整性和一致性。要实现这个目标,推荐你将每次迁移相关的数据库操作封装到一个事务中去。

一个实现事务化迁移更简单的方法是将迁移代码主在safeUp()和safeDown()方法中,这两个方法不同于up()和down()之处在于,它们是完全封装于事务中的。因此,如果在这些方法中有任何失败,之前的所有操作都将被自动回滚。 在下例中,除了创建一个news表外,我们还插入了一个初始化行记录到这个表中:

use yii\db\Migration;
class m150101_185401_create_news_table extends Migration
{
    public function safeUp()
    {
        $this->createTable('news', [
            'id' => $this->primaryKey(),
            'title' => $this->string()->notNull(),
            'content' => $this->text(),
        ]);

        $this->insert('news', [
            'title' => 'test 1',
            'content' => 'content 1',
        ]);
    }
    public function safeDown()
    {
        $this->delete('news', ['id' => 1]);
        $this->dropTable('news');
    }
}

注意通常当你在safeUp()方法中执行多个DB操作时,你应该在safeDown()中掉转它们的执行顺序。在上例中我们首先在safeUp()中创建了表然后插入了一行记录,在safeDown()中我们先删除了行记录然后再删除表。

注意:不是所有的DBMS都支持事务,一些数据查询也不能被放到事务中来,更多实例,请参考隐式提交(implicit commit): https://dev.mysql.com/doc/refman/5.7/en/implicit-commit.html 在此种情况下,你应该使用up()或down()。

//数据库存取方法(Database Accessing Methods) 基础迁移类yii\db\Migration提供了一组从数据库获取和操作的方法。你会发现这些方法的命名与yii\db\Command类提供的DAO方法相似。例如:yii\db\Migration::createTable()方法允许你创建一个新表,与yii\db\Command::createTable()类似。 使用yii\db\Migration提供的方法的好处是,你无需明确创建yii\db\Command实例,每个方法执行时都会自动显示有用的信息,告诉你哪个数据库操作已完成,还需要多长时间。

以下列出了所有的数据库存取操作方法: execute(),执行一个SQL语句 insert(),插入一行 batchInsert(),插入多行 update(),更新多行记录 delete(),删除多行记录 createTable(),创建一个表 renameTable(),重命名一个表 dropTable(),删除一个表 truncateTable(),删除表中的所有记录 addColumn(),添加一列 renameColumn(),重命名列 dropColumn(),删除一列 alterColumn(),修改列关系(外键之类的) addPrimaryKey(),添加一个主键 dropPrimaryKey(),删除一个主键 addForeignKey(),添加一个外键 dropForeignKey(),删除一个外键 createIndex(),创建一个索引 dropIndex(),删除一个索引 addCommentOnColumn(),为列添加注释 dropCommentFromColumn(),删除列注释 addCommentOnTable(),为表添加注释 dropCommentFromTable(),删除列注释

信息:yii\db\Migration没有提供数据库查询方法,这是因为你从数据库取回数据时无需显示额外的信息,也因为你可以使用Query Builder构建和运行更为复杂的查询语句。

注意:当使用迁移处理数据时,你可能发现使用你的Active Record类做此项操作更好使,因为在AR中已经有了实现逻辑。记住,迁移中编写的代码是永远不变的,与此不同的是,应用逻辑将随着业务的改变而改变。在迁移代码中使用AR类,如果AR层逻辑被修改后,将导致现存迁移的意外终止,正因如此,迁移应保持独立,以与其他应用逻辑如AR类相分离。

(全文完)

觉得很赞
    没有找到数据。
您需要登录后才可以回复。登录 | 立即注册