Toidicode.com

Toidicode.com

BASIC TO ADVANCE

Bài 12: Database và Migration trong Laravel 13

Ở bài trước project mới của TaskFlow vẫn được lưu tạm trong session, hết session là mất. Tiếp tục với series, bài này mình sẽ giới thiệu với mọi người về database và migration trong Laravel 13: cấu hình kết nối, tạo và sửa bảng bằng migration, rồi tạo hai bảng projects, tasks cho TaskFlow.

1. Cấu hình database.

Laravel 13 hỗ trợ sẵn MariaDB 10.3+, MySQL 5.7+, PostgreSQL 10+, SQLite 3.26+ và SQL Server 2017+. Các kết nối (connection) được khai báo trong config/database.php, connection mặc định lấy từ biến DB_CONNECTION trong .env.

Khi cài đặt ở Bài 2, installer đã chọn SQLite và tạo file database/database.sqlite. Connection sqlite mặc định đọc đúng file này nên .env chỉ cần một dòng DB_CONNECTION=sqlite. Với SQLite, khóa ngoại được bật sẵn (biến DB_FOREIGN_KEYS).

Muốn chuyển sang MySQL 8.4, các bạn tạo database rồi khai báo thông tin kết nối trong .env:

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=taskflow
DB_USERNAME=taskflow
DB_PASSWORD=secret

PHP cần có extension pdo_mysql. Sau đó chạy php artisan migrate để tạo các bảng trong database mới. Lệnh php artisan db:show in ra loại database, connection, tên database và các bảng (với MySQL cần thêm extension intl).

TaskFlow vẫn dùng SQLite cho gọn, mọi migration trong bài mình đều đã chạy thử trên cả MySQL 8.4.

2. Migration là gì?

Migration giống như version control cho database: cấu trúc bảng được viết bằng code PHP, commit cùng project, ai pull về chỉ cần chạy một lệnh là có database giống nhau.

Các bạn tạo migration với cú pháp:

php artisan make:migration create_projects_table
#  INFO  Migration [database/migrations/2026_11_14_145726_create_projects_table.php] created successfully.

Tên file có tiền tố là thời điểm tạo để Laravel chạy migration theo đúng thứ tự. Laravel còn đoán tên bảng từ tên migration: create_xxx_table sinh sẵn Schema::create('xxx'), còn add_..._to_xxx_table sinh Schema::table('xxx') để sửa bảng có sẵn. File được tạo (bỏ bớt comment):

<?php

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

return new class extends Migration
{
    public function up(): void
    {
        Schema::create('projects', function (Blueprint $table) {
            $table->id();
            $table->timestamps();
        });
    }

    public function down(): void
    {
        Schema::dropIfExists('projects');
    }
};

Trong đó:

  • up() chạy khi migrate: tạo bảng, thêm cột, thêm index.
  • down() chạy khi rollback, làm ngược lại những gì up() đã làm.
  • Schema::create($table, $callback) tạo bảng, các cột được khai báo qua object Blueprint $table.

Ba migration 0001_01_01_... tạo bảng users, cache, jobs... có sẵn từ khi cài đặt và đã được installer chạy.

3. Kiểu cột và modifier.

Blueprint có hơn 60 method tạo cột, dưới đây là các method hay dùng:

  • id(): khóa chính tự tăng, kiểu BIGINT UNSIGNED.
  • string('name', 100): VARCHAR, độ dài mặc định 255. text(): chuỗi dài.
  • integer(), bigInteger(), boolean(), decimal('price', 8, 2), json().
  • date(), dateTime(), timestamp(): ngày, ngày giờ.
  • timestamps(): hai cột created_at, updated_at. softDeletes(): cột deleted_at dùng cho soft delete ở Bài 16.

Gọi nối tiếp sau cột là các modifier: nullable() cho phép NULL, default($value) giá trị mặc định, unique() và index() tạo index. VD:

$table->string('email')->unique();
$table->boolean('is_public')->default(false);
$table->string('note')->nullable();
$table->integer('position')->index();

4. Khóa ngoại và bảng của TaskFlow.

Method foreignId('user_id') tạo cột BIGINT UNSIGNED, nối thêm constrained() để tạo khóa ngoại. Theo quy ước, Laravel bỏ hậu tố _id rồi chuyển sang số nhiều để ra bảng được tham chiếu: user_id trỏ tới users.id. Tên cột không theo quy ước thì truyền tên bảng: constrained('users').

Khi bản ghi cha bị xóa, khóa ngoại xử lý theo:

  • cascadeOnDelete(): xóa luôn các bản ghi con.
  • nullOnDelete(): đặt khóa ngoại về NULL (cột phải nullable()).
  • restrictOnDelete(): không cho xóa khi còn bản ghi con.

Lưu ý: Các modifier như nullable() phải gọi trước constrained().

VD: Mình tạo thêm migration create_tasks_table rồi viết hai bảng của TaskFlow. Cột user_id (chủ project) tạm cho phép NULL vì đến Bài 21 mới có đăng nhập.

- database/migrations/2026_11_14_145726_create_projects_table.php

Schema::create('projects', function (Blueprint $table) {
    $table->id();
    // chủ project, tạm cho phép null vì chưa có đăng nhập (Bài 21)
    $table->foreignId('user_id')->nullable()->constrained()->nullOnDelete();
    $table->string('name', 100);
    $table->text('description')->nullable();
    $table->timestamps();
});

- database/migrations/2026_11_14_145728_create_tasks_table.php

Schema::create('tasks', function (Blueprint $table) {
    $table->id();
    // xóa project thì xóa luôn các task của nó
    $table->foreignId('project_id')->constrained()->cascadeOnDelete();
    $table->string('title');
    $table->text('description')->nullable();
    $table->string('status')->default('todo');
    $table->date('due_date')->nullable();
    // người được giao task, xóa user thì task thành chưa giao
    $table->foreignId('assignee_id')->nullable()->constrained('users')->nullOnDelete();
    $table->timestamps();
});

status tạm là chuỗi với giá trị mặc định todo, đến Bài 19 mình sẽ cast nó sang enum TaskStatus. down() của hai file giữ nguyên Schema::dropIfExists(). File projects tạo trước nên bảng projects có trước khi tasks tham chiếu tới.

5. Chạy và rollback migration.

Lệnh migrate chạy tất cả migration chưa chạy, migrate:status cho biết migration nào đã chạy:

php artisan migrate
#  2026_11_14_145726_create_projects_table ....................... 22.51ms DONE
#  2026_11_14_145728_create_tasks_table ........................... 8.03ms DONE

php artisan migrate:status
#  ...
#  0001_01_01_000002_create_jobs_table ................................ [1] Ran
#  2026_11_14_145726_create_projects_table ............................ [2] Ran
#  2026_11_14_145728_create_tasks_table ............................... [2] Ran

Laravel ghi lại các migration đã chạy trong bảng migrations. Số trong ngoặc là batch: các migration chạy chung một lần migrate thuộc cùng một batch. Các lệnh hay dùng khác:

  • migrate:rollback: chạy down() của batch cuối cùng. Thêm --step=2 để rollback 2 migration gần nhất.
  • migrate:refresh: rollback toàn bộ rồi migrate lại.
  • migrate:fresh: xóa hết các bảng (không qua down()) rồi migrate lại.

Chú ý: migrate:refresh và migrate:fresh xóa toàn bộ dữ liệu, chỉ dùng ở local. Trên production, các lệnh migrate sẽ hỏi xác nhận trước khi chạy (thêm --force để bỏ qua khi deploy).

Mình thử luôn khóa ngoại: xóa user thì user_id, assignee_id thành NULL; xóa project thì các task của nó bị xóa theo; thêm task với project_id không tồn tại sẽ bị lỗi FOREIGN KEY constraint failed.

6. Sửa bảng bằng migration mới.

Migration đã chạy (trên máy đồng đội hay trên server) thì không sửa lại nữa, vì Laravel sẽ không chạy lại nó. Muốn thay đổi bảng, các bạn tạo migration mới.

VD: Thêm cột completed_at lưu thời điểm task hoàn thành.

php artisan make:migration add_completed_at_to_tasks_table

- database/migrations/2026_11_14_145740_add_completed_at_to_tasks_table.php

public function up(): void
{
    Schema::table('tasks', function (Blueprint $table) {
        // thời điểm task được hoàn thành
        $table->timestamp('completed_at')->nullable()->after('due_date');
    });
}

public function down(): void
{
    Schema::table('tasks', function (Blueprint $table) {
        $table->dropColumn('completed_at');
    });
}

after('due_date') đặt cột ngay sau due_date, chỉ có tác dụng với MySQL/MariaDB (SQLite thêm cột vào cuối bảng). Cột mới phải nullable() hoặc có default() vì bảng có thể đã có dữ liệu.

php artisan migrate
#  2026_11_14_145740_add_completed_at_to_tasks_table ............. 12.90ms DONE

php artisan migrate:rollback
#  2026_11_14_145740_add_completed_at_to_tasks_table ............. 14.72ms DONE

Rollback chỉ chạy down() của batch cuối (batch 3) nên chỉ cột completed_at bị xóa. Chạy migrate để thêm lại.

7. Lời kết.

Như vậy chúng ta đã tìm hiểu xong database và migration trong Laravel 13: cấu hình SQLite/MySQL, tạo migration, kiểu cột, khóa ngoại, migrate và rollback. Các bạn xem thêm tài liệu về database và migration. Hai bảng của TaskFlow hiện vẫn trống, bài sau mình sẽ giới thiệu về seeder và factory để tạo dữ liệu mẫu cho chúng.

Đăng ký nhận tin.

Chúng tôi chỉ gửi tối đa 2 lần trên 1 tháng. Tuyên bố không spam mail!

Vũ Thanh Tài

About author
The best way to learn is to share
Xem tất cả bài đăng

0 Comments

Bài viết chưa có ai bình luận, hãy là người đầu tiên đi bạn!

Bình luận

Captcha