Có bao giờ anh em dồng dev nhận dự án của một ai đó xong phải tạo lại dữ liệu chưa ? Chắc chắn là sẽ có ít nhất đôi lần, trước đây tại hạ cũng vậy và rất cay cú trong việc phải setup dữ liệu để chạy dự án. Nhưng từ khi sử dụng Yii2 và trước đó là Laravel thì tại hạ đã sử dụng migrate để giải quyết việc này. Ngoài ra thì còn 1 cách nữa là dùng Diagram của Mysql Workbench để đồng bộ lại dữ liệu.

Trong quá trình phát triển ứng dụng web thì việc định nghĩa ra 1 cấu trúc cơ sở dữ liệu là rất cần thiết. Migration chính là công cụ hoàn hảo để đảm nhiệm việc đó, nó giúp các đồng dev khi maintain hoặc cập nhật một ứng dụng nào đó rất nhanh mà không cần phải thao tác quá nhiều với hệ quản trị cơ sở dữ liệu.

Tạo Migration trong Yii2

Trước khi sử dụng migration thì các đồng dev phải lưu ý là đã kết nối được với cơ sở dữ liệu rồi nhé, tức là đã tạo 1 cơ sở dữ liệu trống và kết nối thành công rồi

Để tạo một migration mới, các đồng dev chạy lệnh command sau:

./yii migrate/create <name>

Ở đây <name> chính là tên của migration cần tao. Ví dụ, tại hạ sẽ tạo 1 migration Blog như sau

./yii migrate/create create_table_blog

yii2 migrate

Như vậy là đã tạo ra được file migration cho bảng Blog, để xem có gì trong file này nhé, file này sẽ được tạo ra ở console/migrations

yii2 migrate

Nó tạo ra với tên là m210709_202148_create_table_blog.php phần m210709_202148 là phần thời gian được Yii2 tự đặt tên theo định dạng m<YYMMDD_HHMMSS>_<Name>. Để xem nội dung file có gì nhé

<?php

use yii\db\Migration;

/**
 * Class m210709_202148_create_table_blog
 */
class m210709_202148_create_table_blog extends Migration
{
    /**
     * {@inheritdoc}
     */
    public function safeUp()
    {

    }

    /**
     * {@inheritdoc}
     */
    public function safeDown()
    {
        echo "m210709_202148_create_table_blog cannot be reverted.\n";

        return false;
    }

   
    // Use up()/down() to run migration code without a transaction.
    public function up()
    {

    }

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

        return false;
    }
    
}

Ở đây 2 phương thức up/down mặc định tạo ra sẽ bị comment lại và tại hạ đã mở comment ra. Lý do 2 phương thức này mặc đinh bị comment là do 2 phương thức này sẽ không chạy trong transaction nếu có lỗi sẽ không thể rollback lại như 2 phương thức safeUp()safeDown() được. Để chay thì tại hạ vẫn thường dùng up/down là chính =)).

Với phương thức up() thì chủ yếu định nghĩa các câu truy vấn tạo hoặc cập nhật cấu trúc cơ sở dữ liệu còn down() thì ngược lại, nó có thể xóa các cấu trúc được thêm hoặc cập nhật từ up().

Ví dụ tại hạ sẽ thêm vào bảng Blog các trường như sau:

<?php

use yii\db\Migration;

class m210709_202148_create_table_blog extends Migration
{
    public function up()
    {
        $this->createTable('blog', [
            'id' => $this->primaryKey(),
            'title' => $this->string()->notNull(),
            'content' => $this->text(),
        ]);
    }

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

Và chạy lệnh command sau để migrate thêm vào cơ sở dũ liệu

./yii migrate

yii2 migrate

Và đã có bảng Blog trong cơ sở dữ liệu

yii2 migration

yii2 migrate

Nó sẽ tạo ra 3 bảng là blog, user, migration. Do mặc đinh khi khởi tạo project Yii2 đã cung cấp sẵn 2 file migration cho user, còn bảng migration chính là lưu thông tin thời gian chạy migrate các file migration

Để chạy riêng biệt mà không phải chạy tất cả các file migration thì các đồng dev có thể sử dụng các command sau:

// Ví dụ với file m210709_202148_create_table_blog.php này

./yii migrate/to 210709_202148                      # sử dụng thời gian trong tên file 
./yii migrate/to "2021-07-09 18:54:01"              # sử dụng thời gian sau khi đã format lại thông qua strtotime()
./yii migrate/to m210709_202148_create_table_blog   # sử dụng tên file migration -> nên dùng cách này
./yii migrate/to 1392853618                         # sử dụng thời gian dạng UNIX

Đôi khi, các đồng dev có thể chỉ muốn áp dụng một hoặc một số migrate mới, thay vì tất cả các đồng dev có thể làm như vậy bằng cách chỉ định số lần migrate muốn áp dụng khi chạy lệnh. Ví dụ: lệnh sau sẽ cố gắng áp dụng 2 lần migrate đã có sẵn:

Giả sử là tại hạ có 3 lần migrate là tạo bảng blog, user, category thì theo trình tự nếu migrate 2 lần gần nhất thì chỉ chạy cho blog và user nếu file migrate của 2 bảng này có sự thay đổi

./yii migrate 2

Nếu như ./yii migrate để chạy cho hàm up() trong file migration thì để chạy cho hàm down chúng ta cũng có cú pháp gần giống như sau

./yii migrate/down     # Hoàn lại các migrate gần nhất, ví dụ nếu đã tạo bảng blog thì sẽ xóa đi nếu chạy lệnh này
./yii migrate/down 3   # Tương tự sẽ hoàn nguyên lại 3 lần migrate gần nhất, ví dụ 3 lần gần nhất là tạo bảng user, blog, category thì nó sẽ xóa cả 3 bảng này

Để làm lại các migrate sử dụng command:

./yii migrate/redo        # redo the last applied migration
./yii migrate/redo 3      # redo the last 3 applied migrations

Để làm mới migrate

./yii migrate/fresh       # truncate the database and apply all migrations from the beginning

Để liệt kê lịch sử thao tác migrate

./yii migrate/history     # showing the last 10 applied migrations
./yii migrate/history 5   # showing the last 5 applied migrations
./yii migrate/history all # showing all applied migrations

./yii migrate/new         # showing the first 10 new migrations
./yii migrate/new 5       # showing the first 5 new migrations
./yii migrate/new all     # showing all new migrations

Để dánh dấu các migration mà các đồng dev không muốn chạy trong migrate thì sử dụng command sau

// Ví dụ với file m210709_202148_create_table_blog.php này

./yii migrate/mark 210709_202148                      # sử dụng thời gian trong tên file 
./yii migrate/mark "2021-07-09 18:54:01"              # sử dụng thời gian sau khi đã format lại thông qua strtotime()
./yii migrate/mark m210709_202148_create_table_blog   # sử dụng tên file migration -> nên dùng cách này
./yii migrate/mark 1392853618                         # sử dụng thời gian dạng UNIX

Bổ sung thêm 1 chút về lệnh tạo migration thì Yii có cung cấp các cấu trúc để tạo tiện lợi như sau:

Vẫn là mã lệnh ./yii migrate/create <name> nhưng nếu <name> có dạng đặc biệt là create_xxx_table hoặc drop_xxx_table thì file migration sẽ chứa các mã được gen ra tự động như sau, ví dụ vẫn tạo với BLog

./yii migrate/create create_blog_table

Nó sẽ tạo ra 1 file migration có nội dung như sau

class m210710_220037_create_blog_table extends Migration
{
    /**
     * {@inheritdoc}
     */
    public function up()
    {
        $this->createTable('blog', [
            'id' => $this->primaryKey()
        ]);
    }

    /**
     * {@inheritdoc}
     */
    public function down()
    {
        $this->dropTable('blog');
    }
}

Hoặc có thể sử dụng option --fields để add thêm các field vào như sau

./yii migrate/create create_blog_table --fields="title:string,body:text"

FIle migration được tạo ra

class m210710_220037_create_blog_table extends Migration
{
    /**
     * {@inheritdoc}
     */
    public function up()
    {
        $this->createTable('blog', [
            'id' => $this->primaryKey(),
            'title' => $this->string(),
            'body' => $this->text(),
        ]);
    }

    /**
     * {@inheritdoc}
     */
    public function down()
    {
        $this->dropTable('blog');
    }
}

Sử dụng với các tham số có kiểu dữ liệu 

./yii migrate/create create_post_table --fields="title:string(12):notNull:unique,body:text"

File được tạo ra

class m210710_220037_create_blog_table extends Migration
{
    /**
     * {@inheritdoc}
     */
    public function up()
    {
        $this->createTable('blog', [
            'id' => $this->primaryKey(),
            'title' => $this->string(12)->notNull()->unique(),
            'body' => $this->text()
        ]);
    }

    /**
     * {@inheritdoc}
     */
    public function down()
    {
        $this->dropTable('blog');
    }
}

Sử dụng khi có khóa ngoại 

./yii migrate/create create_post_table --fields="author_id:integer:notNull:foreignKey(user),category_id:integer:defaultValue(1):foreignKey,title:string,body:text"

File migration có các liên kết đến khóa ngoại như sau

class m210710_040430_create_blog_table extends Migration
{
    /**
     * {@inheritdoc}
     */
    public function up()
    {
        $this->createTable('blog', [
            'id' => $this->primaryKey(),
            'author_id' => $this->integer()->notNull(),
            'category_id' => $this->integer()->defaultValue(1),
            'title' => $this->string(),
            'body' => $this->text(),
        ]);

        // creates index for column `author_id`
        $this->createIndex(
            'idx-blog-author_id',
            'blog',
            'author_id'
        );

        // add foreign key for table `user`
        $this->addForeignKey(
            'fk-blog-author_id',
            'blog',
            'author_id',
            'user',
            'id',
            'CASCADE'
        );

        // creates index for column `category_id`
        $this->createIndex(
            'idx-blog-category_id',
            'blog',
            'category_id'
        );

        // add foreign key for table `category`
        $this->addForeignKey(
            'fk-blog-category_id',
            'blog',
            'category_id',
            'category',
            'id',
            'CASCADE'
        );
    }

    /**
     * {@inheritdoc}
     */
    public function down()
    {
        // drops foreign key for table `user`
        $this->dropForeignKey(
            'fk-blog-author_id',
            'blog'
        );

        // drops index for column `author_id`
        $this->dropIndex(
            'idx-blog-author_id',
            'blog'
        );

        // drops foreign key for table `category`
        $this->dropForeignKey(
            'fk-blog-category_id',
            'blog'
        );

        // drops index for column `category_id`
        $this->dropIndex(
            'idx-blog-category_id',
            'blog'
        );

        $this->dropTable('blog');
    }
}

Dưới đây là danh sách tất cả các phương thức truy cập cơ sở dữ liệu này:

execute () : thực thi một câu lệnh SQL

insert () : chèn một hàng

batchInsert () : chèn nhiều hàng

update () : cập nhật hàng

upsert () : chèn một hàng hoặc cập nhật nó nếu nó tồn tại (kể từ 2.0.14)

delete () : xóa hàng

createTable () : tạo bảng

renameTable () : đổi tên bảng

dropTable () : xóa bảng

truncateTable () : xóa tất cả các hàng trong bảng

addColumn () : thêm một cột

renameColumn () : đổi tên cột

dropColumn () : xóa một cột

AlterColumn () : thay đổi một cột

addPrimaryKey () : thêm khóa chính

dropPrimaryKey () : xóa khóa chính

addForeignKey () : thêm khóa ngoại

dropForeignKey () : xóa khóa ngoại

createIndex () : tạo chỉ mục

dropIndex () : xóa chỉ mục

addCommentOnColumn () : thêm comment vào cột

dropCommentFromColumn () : bỏ comment từ cột

addCommentOnTable () : thêm comment vào bảng

dropCommentFromTable () : bỏ comment khỏi bảng

Để chi tiết hơn về các phương thức thao tác với cơ sở dũ liệu, các đồng dev tham khảo thêm tại đây

Tổng kết 

Migration rất tiện cho cho các anh em đồng dev trong quá trình phát triển và duy trì cấu trúc cơ sở dũ liệu của ứng dụng. Nó rút ngắn được thời gian triển khai ứng dụng trên các môi trường và đem lại sự hiệu quả cao. Ngoài migration này ra, tại hạ sẽ hướng dẫn các đồng dev sử dụng 1 tool nữa đó là Mysql Workbench để sync các cấu trúc cơ sở dũ liệu một cách nhanh nhất =)), về cơ bản cơ chế nó không khác gì migration lắm. Bài viết có thể chưa được hoàn thiện lắm mong anh em đồng dev thông cảm.