Cơ sở dữ liệu: Migrations

Giới thiệu

Migration được coi như là version control cho database, cho phép team có thể dễ dàng thay đổi và chia sẻ schema của database trong chương trình với nhau. Migratiom cơ bản được sử dụng cùng với schema builder để dễ dàng xây dựng cấu trúc cho database schema. Nếu bạn đã gặp vấn đề khi thêm một cột vào local của các đồng đội trong team, migration sẽ xử lý vấn đề này rất dễ dàng.

Schema facade hỗ trợ việc tạo và thao tác trên các bảng mà không cần biết về database, tương ứng và mạch lạc khi giao tiếp với các hệ thống database khác nhau mà Laravel hỗ trợ.

Tạo Migrations

Để tạo một migration, sử dụng câu lệnh make:migration Artisan:

php artisan make:migration create_users_table

File migration mới sẽ được đặt trong thư mục database/migrations. Mỗi file migration được đặt tên bao gồm timestamp để xác định thứ tự các migration với nhau.

Tham số --table--create có thể được sử dụng để cho biết table nào và migration có cần tạo table mới hay không. Hai tham số này đơn giản được dùng để cho mã stub được tạo ra sẽ thực hiện trên table nào:

php artisan make:migration create_users_table --create=users

php artisan make:migration add_votes_to_users_table --table=users

Nếu bạn muốn đưa migration vào trong một thư mục khác, bạn có thể truyền vào tham số --path khi thực hiện lệnh make:migration. Đường dẫn đưa vào phải relative với đường dẫn cơ bản của chương trình.

Cấu trúc migration

Một migration class chứa hai hàm cơ bản là: updown. Hàm up được dùng để tạo table, cột hay index mới vào trong database, trong khi hàm down đơn giản chỉ dùng để làm ngược lại những thao tác ở hàm up.

Bên trong hai hàm này bạn có thể sử dụng schema builder để tạo và chỉnh sửa table rõ ràng hơn. Để tìm hiểu tất cả các hàm được cung cấp trong Schema builder, xem tại đây. Ví dụ, hãy cùng nhau làm một ví dụ về migration bằng việc tạo ra một bảng tên là flights:

<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateFlightsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return  void
     */
    public function up()
    {
        Schema::create('flights', function (Blueprint $table) {
            $table->increments('id');
            $table->string('name');
            $table->string('airline');
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return  void
     */
    public function down()
    {
        Schema::drop('flights');
    }
}

Thực thi Migrations

Để thực thi tất cả các migration trong chương trình, sử dụng lệnh migrate Artisan:

php artisan migrate

Nếu bạn đang sử dụng Homestead virtual machine, bạn cần chạy câu lệnh này bên trong đó.

Ép buộc thực thi migration chạy cho môi trường production

Một số thao tác migration khá là nguy hiểm, vì chúng có thể gây ra mất mát dữ liệu. Để tránh khỏi điều này khi thực thi các câu lệnh migration trong môi trường production, bạn sẽ nhận được yêu cầu xác nhận trước khi thực chi chúng. Để ép các câu lệnh này thực thi mà không cần xác nhận, sử dụng cờ --force:

php artisan migrate --force

Rolling Back Migrations

Để rollback lại thao tác migration cuối cùng, bạn có thể sử dụng câu lệnh rollback. Lệnh sẽ rolls back lại row có "batch" lớn nhất của bảng migrations, có thể bao gồm một hoặc nhiều file migration:

php artisan migrate:rollback

Bạn có thế giới hạn số file migrations rollback bằng thêm step vào lệnh rollback. Ví dụ, lệnh sau sẽ rollback 5 file sau cùng của bảng migrations có "batch" lớn nhất:

php artisan migrate:rollback --step=5

Lệnh migrate:reset sẽ thực hiện rollback lại toàn bộ migration của chương trình:

php artisan migrate:reset

Rollback & Migrate trong một câu lệnh

Lệnh migrate:refresh sẽ đầu tiên rollback lại toàn bộ migration của chương trình, và thực hiện câu lệnh migrate. Câu lệnh sẽ thực hiện tái cấu trúc toàn bộ database:

php artisan migrate:refresh

// Refresh the database and run all database seeds...
php artisan migrate:refresh --seed

Bạn có thẻ rollback & migrate lại với giới hạn migrations bởi cờ step vào lệnh refresh. Ví dụ, lệnh sau sẽ rollback & migrate lại 5 file mới nhất của migrations:

php artisan migrate:refresh --step=5

Bảng

Tạo bảng

Để tạo một bảng mới, sử dụng hàm create trong Schema facade. Hàm create nhận hai tham số. Tham số đầu là tên của bảng, còn tham số thứ hai là một Closure mà sẽ nhận vào một Blueprint object để khai báo cấu trúc của bảng mới:

Schema::create('users', function (Blueprint $table) {
    $table->increments('id');
});

Tất nhiên, Khi tạo bảng mới, bạn có thể sử dụng bất cứ hàm tạo cột nào để tạo các trường cho bảng.

Kiểm tra xem bảng hay cột có tồn tại hay không

Bạn có thể dễ dàng kiểm tra sự tồn tại của một bảng hay cột sử dụng hàm hasTable and hasColumn:

if (Schema::hasTable('users')) {
    //
}

if (Schema::hasColumn('users', 'email')) {
    //
}

Connection & Storage Engine

Nếu bạn muốn thực hiện thao tác schema trên một kết nối database không phải mặc định, sử dụng hàm connection:

Schema::connection('foo')->create('users', function ($table) {
    $table->increments('id');
});

Bạn có thể sử dụng thuộc tính engine trên schema builder để định nghĩa storage engine cho bảng:

Schema::create('users', function ($table) {
    $table->engine = 'InnoDB';

    $table->increments('id');
});

Đổi tên / xóa bảng

Để đổi tên một bảng đã tồn tại trong database, sử dụng hàm rename:

Schema::rename($from, $to);

Để drop một bảng trong database, bạn có thể sử dụng hàm drop hoặc dropIfExists:

Schema::drop('users');

Schema::dropIfExists('users');

Đổi tên bảng với foreign keys

Trước khi thay đổi tên bảng, bạn nên kiểm tra xem có foreign key constraints nào trên bảng có tên khác trong migration file hay không thay vì để Laravel tự gán tên. Nếu không, tên của foreign key constraint sẽ trỏ tới tên cũ của bảng.

Columns

Tạo Columns

Hàm table trong Schema facade sử dụng để cập nhật một bảng đã tồn tại. Như hàm create, hàm table nhận hai tham số: tên của bảng và một Closure nhận một Blueprint object để thực hiện thao tác với table:

Schema::table('users', function ($table) {
    $table->string('email');
});

Các kiểu Column

Tất nhiên, the schema builder chứa vài kểu column cho bạn có thể khi tạo bảng:

Command Description
$table->bigIncrements('id'); Tăng ID (primary key) sư dụng như "UNSIGNED BIG INTEGER".
$table->bigInteger('votes'); Tương đương với BIGINT.
$table->binary('data'); Tương đương với BLOB.
$table->boolean('confirmed'); Tương đương với BOOLEAN.
$table->char('name', 4); Tương đương với CHAR với độ dài cho trước.
$table->date('created_at'); Tương đương với DATE.
$table->dateTime('created_at'); Tương đương với DATETIME.
$table->dateTimeTz('created_at'); Tương đương với DATETIME (with timezone).
$table->decimal('amount', 5, 2); Tương đương với DECIMAL với độ chính sách và phần thập phân.
$table->double('column', 15, 8); Tương đương với DOUBLE với độ chính xác, 15 chữ số và 8 ký tự tính sau dấu phảy.
$table->enum('choices', ['foo', 'bar']); Tương đương với ENUM.
$table->float('amount', 8, 2); Tương đương với FLOAT, 8 chữ số and 2 chữ số tính sau dấu phẩy.
$table->increments('id'); Tăng ID (primary key) sử dụng như "UNSIGNED INTEGER".
$table->integer('votes'); Tương đương với INTEGER.
$table->ipAddress('visitor'); Tương đương với IP.
$table->json('options'); Tương đương với JSON.
$table->jsonb('options'); Tương đương với JSONB.
$table->longText('description'); Tương đương với LONGTEXT.
$table->macAddress('device'); Tương đương với MAC.
$table->mediumIncrements('id'); Tăng ID (primary key) sử dụng như "UNSIGNED MEDIUM INTEGER".
$table->mediumInteger('numbers'); Tương đương với MEDIUMINT.
$table->mediumText('description'); Tương đương với MEDIUMTEXT.
$table->morphs('taggable'); Thêm unsigned INTEGER taggable_id và STRING taggable_type.
$table->nullableTimestamps(); Giống như timestamps().
$table->rememberToken(); Thêm remember_token như VARCHAR(100) NULL.
$table->smallIncrements('id'); Tăng ID (primary key) sử dụng như "UNSIGNED SMALL INTEGER".
$table->smallInteger('votes'); Tương đương với SMALLINT.
$table->softDeletes(); Thêm nullable deleted_at column for soft deletes.
$table->string('email'); Tương đương với VARCHAR .
$table->string('name', 100); Tương đương với VARCHAR với độ dài.
$table->text('description'); Tương đương với TEXT.
$table->time('sunrise'); Tương đương với TIME.
$table->timeTz('sunrise'); Tương đương với TIME (với timezone).
$table->tinyInteger('numbers'); Tương đương với TINYINT.
$table->timestamp('added_on'); Tương đương với TIMESTAMP.
$table->timestampTz('added_on'); Tương đương với TIMESTAMP (với timezone).
$table->timestamps(); Thêm nullable created_at và cột updated_at.
$table->timestampsTz(); Thêm nullable created_atupdated_at (với timezone).
$table->unsignedBigInteger('votes'); Tương đương với Unsigned BIGINT.
$table->unsignedInteger('votes'); Tương đương với Unsigned INT.
$table->unsignedMediumInteger('votes'); Tương đương với Unsigned MEDIUMINT.
$table->unsignedSmallInteger('votes'); Tương đương với Unsigned SMALLINT.
$table->unsignedTinyInteger('votes'); Tương đương với Unsigned TINYINT.
$table->uuid('id'); Tương đương với UUID.

Column Modifiers

Ngoài các kiểu column liệt kê ở trên, có một số kiểu column "modifiers" khác mà bạn có thể sử dụng. Ví dụ để làm cho column "nullable", bạn có thể dùng hàm nullable:

Schema::table('users', function ($table) {
    $table->string('email')->nullable();
});

Dưới đây là một số column modifier mà bạn có thể sử dụng, không bao gồm các index modifiers:

Modifier Description
->after('column') Đặt column "after" một column khác (MySQL Only)
->comment('my comment') Thêm một comment cho column.
->default($value) Đặt giá trị "mặc định" vào column
->first() Đặt column "first" vào trong bảng (MySQL Only)
->nullable() Cho phép dữ liệu kiểu NULL có thể chèn vào column.
->storedAs($expression) Tạo một cột stored (MySQL Only)
->unsigned() Đặt cột integer sang UNSIGNED
->virtualAs($expression) Tạo một cột virtual (MySQL Only)

Modifying Columns

Yêu cầu

Trước khi chỉnh sửa column, hãy chắc chắn là bạn thêm vào doctrine/dbal dependency vào trong file composer.json. Thư viện Doctrine DBAL được dùng để xác định trạng thái hiện tại của column và tạo câu SQL query cần thiết để chỉnh sửa column:

composer require doctrine/dbal

Cập nhật thuộc tính của Column

Hàm change cho phép bạn thay đổi một column sang một kiểu mới, hoặc thay đổi thuộc tính của column. Ví dụ, bạn có thể muốn tăng kích thước của string. Xem ví dụ dưới đây trong việc sử dụng change để thay đổi kích thước của column name column từ 25 thành 50:

Schema::table('users', function ($table) {
    $table->string('name', 50)->change();
});

Chúng ta cũng có thể thay đổi một column sang nullable:

Schema::table('users', function ($table) {
    $table->string('name', 50)->nullable()->change();
});

Hiện tại chưa hỗ trợ thay đổi cột có kiểu là enum.

Đổi tên columns

Để thay đổi tên column, bạn có thể sử dụng hàm renameColumn trong Schema builder. Trước khi đổi tên một column, hãy chắc chắn rằng đã thêm doctrine/dbal dependency vào file composer.json:

Schema::table('users', function ($table) {
    $table->renameColumn('from', 'to');
});

Hiện tại chưa hỗ trợ đổi tên cột có kiểu là enum.

Dropping Columns

Để drop một column, sử dụng hàm dropColumn trong the Schema builder. Trước khi dropping columns từ SQLite database, bạn cần phải thêm doctrine/dbal dependency vào file composer.json và chạy lệnh composer update để cài thư viện:

Schema::table('users', function ($table) {
    $table->dropColumn('votes');
});

Bạn có thể drop nhiều columns từ một bảng bằng cách truyền một mảng các tên column vào hàm dropColumn:

Schema::table('users', function ($table) {
    $table->dropColumn(['votes', 'avatar', 'location']);
});

Drop hay thay đổi nhiều columns trong một file migration trong khi sử dụng SQLite chưa được hỗ trợ.

Indexes

Tạo Indexes

Schema builder hỗ trợ vài kiểu index. Đầu tiên, cùng xem một ví dụ tạo column có giá trị là unique. Để tạo index, chúng ta sẽ sử dụng hàm unique khi tạo column:

$table->string('email')->unique();

Ngoài ra bạn có thể tạo index ngay sau khi tạo column, ví dụ:

$table->unique('email');

Bạn có thể truyền vào một mảng column cho hàm :

$table->index(['account_id', 'created_at']);

Laravel sẽ tự thực hiện tạo tên phù hợp cho index, nhưng bạn có thể tự đặt tên cho index bằng cách truyền vào tham số thứ hai:

$table->index('email', 'my_index_name');

Các kiểu Index

Command Description
$table->primary('id'); Thêm vào một primary key.
$table->primary(['first', 'last']); Thêm vào một composite keys.
$table->unique('email'); Thêm vào một unique index.
$table->unique('state', 'my_index_name'); Thêm một index có tên.
$table->index('state'); Thêm vào một index.

Dropping Indexes

Để drop một index, bạn cần truyền vào tên của index. Về mặc định, Laravel sẽ tự động gán một tên phù hợp cho index. Đơn giản chỉ là nối tên bảng, tên của column được index và kiểu index. Dưới đây là một vài ví dụ:

Command Description
$table->dropPrimary('users_id_primary'); Drop một primary key khỏi bảng "users".
$table->dropUnique('users_email_unique'); Drop một unique key khỏi bảng "users".
$table->dropIndex('geo_state_index'); Drop một unique key khỏi bảng "geo".

Nếu bạn truyền vào một mảng các column, thì Laravel sẽ thực hiện tìm tên index tương ứng dựa theo quy tắc đặt để drop:

Schema::table('geo', function ($table) {
    $table->dropIndex(['state']); // Drops index 'geo_state_index'
});

Foreign Key Constraints

Laravel cũng hỗ trợ cung cấp việc tạo foreign key constraint một cách dễ dàng. Ví dụ, cùng tạo một column user_id trong bảng posts tham chiếu tới column id trong bảng users:

Schema::table('posts', function ($table) {
    $table->integer('user_id')->unsigned();

    $table->foreign('user_id')->references('id')->on('users');
});

Bạn cũng có thể chỉ định thao tác cho thuộc tính "on delete" và "on update" của constraint:

$table->foreign('user_id')
      ->references('id')->on('users')
      ->onDelete('cascade');

Để drop một foreign key, bạn có thể sử dụng hàm dropForeign. Foreign key constraints sử dụng chung quy tắc đặt tên như index. Vì thế, chúng ta tên của foreign key constraints sẽ bao gồm tên bảng, tên cột và hậu tố là "_foreign":

$table->dropForeign('posts_user_id_foreign');

Hay bạn có thể truyền vào một mảng các giá trị để thực hiện drop theo quy tắc đặt tên:

$table->dropForeign(['user_id']);

Bạn có thể kích hoạt hay bỏ kích hoạt việc sử dụng foreign key constraint trong migration sử dụng hai hàm sau:

Schema::enableForeignKeyConstraints();

Schema::disableForeignKeyConstraints();
Nguồn: https://laravel.com/docs/5.3/migrations