iT邦幫忙

2024 iThome 鐵人賽

DAY 9
0

哈囉,大家好!經過前面的努力,我們已經為個人財務管理系統設計了完整的資料庫結構。現在,是時候將這些設計實際落地到 Laravel 框架中了。今天,我們要一起探索 Laravel 中非常重要的一環:Migration(資料庫遷移)。

相信我,掌握了 Migration,不僅能讓你更有效地管理資料庫,還能讓你在開發過程中更加游刃有餘。讓我們一起揭開 Migration 的神秘面紗吧!

一、什麼是 Laravel 的 Migration?

Migration 是 Laravel 提供的一種資料庫管理工具,它讓你可以用程式碼的方式定義和修改資料庫結構。簡單來說,Migration 就像是資料庫的版本控制,讓你可以方便地同步團隊之間的資料庫變更。

為什麼要使用 Migration?

  • 版本控制:像 Git 一樣,Migration 讓你可以追蹤資料庫的變化,方便團隊協作。
  • 自動化:透過指令即可執行資料庫的建立、更新,減少手動操作的錯誤風險。
  • 可移植性:輕鬆在不同的環境中同步資料庫結構,如開發、測試、正式環境。

個人經驗分享:還記得剛開始開發時,手動修改資料庫結構總是帶來各種麻煩。自從使用了 Migration,一切變得有條不紊,在開發上也更加順暢。

二、開始使用 Migration

1. 建立 Migration 檔案

Laravel 提供了 Artisan CLI(命令列介面)來幫助我們生成 Migration 檔案。讓我們來為 users 表建立一個 Migration。
在終端機中進入專案的根目錄,執行以下指令:等等……你有發現 database/migrations 目錄下,已經有一個 create_users_table.php 檔案嗎?
這個檔案就是預設的 users 資料表結構:

/**
 * Run the migrations.
 */
public function up(): void
{
    Schema::create('users', function (Blueprint $table) {
        $table->id();
        $table->string('name');
        $table->string('email')->unique();
        $table->timestamp('email_verified_at')->nullable();
        $table->string('password');
        $table->rememberToken();
        $table->timestamps();
    });

    Schema::create('password_reset_tokens', function (Blueprint $table) {
        $table->string('email')->primary();
        $table->string('token');
        $table->timestamp('created_at')->nullable();
    });

    Schema::create('sessions', function (Blueprint $table) {
        $table->string('id')->primary();
        $table->foreignId('user_id')->nullable()->index();
        $table->string('ip_address', 45)->nullable();
        $table->text('user_agent')->nullable();
        $table->longText('payload');
        $table->integer('last_activity')->index();
    });
}

/**
 * Reverse the migrations.
 */
public function down(): void
{
    Schema::dropIfExists('users');
    Schema::dropIfExists('password_reset_tokens');
    Schema::dropIfExists('sessions');
}

這比我們預先規劃的多了兩張表,而且 users 的結構有幾個地方不一樣。所以在我們開始編輯 Migration 檔案前,先來介紹 Schema Builder 的相關用法。

2. Schema Builder 的基本用法

Laravel 的 Schema Builder 是一個強大的工具,讓你可以使用 PHP 語法來定義資料庫的結構,而不需要直接撰寫 SQL 語句。這使得程式碼更具可讀性,也更容易維護。
以下是一些常用的 Schema Builder 方法,以及它們對應的 MySQL 資料型態:

  • $table->bigIncrements('id'):建立一個自增的 BIGINT 主鍵欄位,等同於 MySQL 的 BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY。
  • $table->string('username', 50)->unique():建立一個長度為 50 的 VARCHAR 欄位,並設定唯一索引。
  • $table->string('password'):建立一個字串欄位,用於儲存加密後的密碼,等同於 MySQL 的 VARCHAR(255)。
  • $table->timestamps():自動建立 created_at 和 updated_at 兩個時間戳記欄位。
  • $table->text('description'):建立一個 TEXT 欄位,適合儲存大量文字。
  • $table->decimal('amount', 8, 2):建立一個 DECIMAL 欄位,總長度為 8 位,包含 2 位小數。
  • $table->boolean('is_active'):建立一個 BOOLEAN 欄位,儲存布林值。
  • $table->date('transaction_date'):建立一個 DATE 欄位,用於儲存日期。
  • $table->enum('type', ['income', 'expense']):建立一個 ENUM 枚舉欄位,限定值為 'income' 或 'expense'。
  • $table->foreignId('user_id')->constrained('users'):建立一個外鍵欄位,參照 users 表的 id 欄位。
  • $table->unique('email'):為 email 欄位建立唯一索引。
    MySQL 資料型態對照
  • $table->string():VARCHAR
  • $table->text():TEXT
  • $table->integer():INT
  • $table->bigInteger():BIGINT
  • $table->decimal():DECIMAL
  • $table->boolean():TINYINT(1)
  • $table->date():DATE
  • $table->dateTime():DATETIME

小提醒:Laravel 的 Schema Builder 提供了許多方便的方法,詳細可以參考官方文件,多加了解絕對不會吃虧!

3. 編寫 Migration 檔案

打開預設的 create_users_table.php 檔案,你會看到 up 和 down 兩個方法。

  • up 方法:定義在執行 Migration 時要做的事情,例如建立資料表。
  • down 方法:定義在回滾 Migration 時要做的事情,例如刪除資料表。
    現在,讓我們修改 up 方法,使其符合我們的需求:
public function up()
{
    Schema::create('users', function (Blueprint $table) {
        $table->bigIncrements('id');
        $table->string('username', 50)->unique();
        $table->string('email', 100)->unique();
        $table->string('password');
        $table->timestamps();
    });
}

在這裡,我們使用了 Laravel 的 Schema Builder 來定義資料表的結構,並移除了我們不需要的欄位。

寫下來我們所執行的指令都是在Laravel 容器內執行,如果忘記怎麼進入容器的終端環境,請回去參考D5喔

三、為其他資料表建立 Migration

接下來,按照同樣的步驟,為其他資料表建立 Migration。

1. 建立 bank_accounts 表的 Migration

執行指令:

php artisan make:migration create_bank_accounts_table --create=bank_accounts

編輯生成的 Migration 檔案:

public function up()
{
    Schema::create('bank_accounts', function (Blueprint $table) {
        $table->bigIncrements('id');
        $table->unsignedBigInteger('user_id');
        $table->string('account_name', 100);
        $table->string('account_number', 50)->nullable();
        $table->string('bank_name', 100)->nullable();
        $table->decimal('balance', 15, 2)->default(0.00);
        $table->timestamps();

        // 外鍵約束
        $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
    });
}

2. 建立 categories 表的 Migration

執行指令:

php artisan make:migration create_categories_table --create=categories

編輯生成的 Migration 檔案:

public function up()
{
    Schema::create('categories', function (Blueprint $table) {
        $table->bigIncrements('id');
        $table->unsignedBigInteger('user_id');
        $table->string('category_name', 100);
        $table->enum('type', ['income', 'expense']);
        $table->timestamps();

        // 外鍵約束
        $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
    });
}

3. 建立 transactions 表的 Migration

執行指令:

php artisan make:migration create_transactions_table --create=transactions

編輯生成的 Migration 檔案:

public function up()
{
    Schema::create('transactions', function (Blueprint $table) {
        $table->bigIncrements('id');
        $table->unsignedBigInteger('user_id');
        $table->unsignedBigInteger('bank_account_id');
        $table->unsignedBigInteger('category_id');
        $table->enum('type', ['income', 'expense']);
        $table->decimal('amount', 15, 2);
        $table->date('transaction_date');
        $table->string('description', 255)->nullable();
        $table->timestamps();

        // 外鍵約束
        $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
        $table->foreign('bank_account_id')->references('id')->on('bank_accounts')->onDelete('cascade');
        $table->foreign('category_id')->references('id')->on('categories')->onDelete('cascade');
    });
}

四、執行 Migration

所有 Migration 檔案都準備好了,現在讓我們來執行它們,實際建立資料表。
在終端機中執行:

php artisan migrate

如果一切順利,終端機會顯示 Migration 已成功執行。

個人經驗分享:有時候執行 Migration 時會遇到錯誤,通常是因為資料表之間的關聯順序問題。例如,transactions 表需要 users、bank_accounts 和 categories 表已經存在。所以在編寫 Migration 時,注意資料表的建立順序很重要。

解決關聯順序問題

為了確保 Migration 按照正確的順序執行,可以在 Migration 檔案的開頭加上時間戳,使其按照時間順序排列。
例如,在 Migration 檔案名稱前面添加時間戳:

  • 2024_09_22_100000_create_users_table.php
  • 2024_09_22_101000_create_bank_accounts_table.php
  • 2024_09_22_102000_create_categories_table.php
  • 2024_09_22_103000_create_transactions_table.php
    這樣,Laravel 會按照時間順序執行 Migration,避免外鍵約束的問題。

五、Rollback 與修改 Migration

1. 回滾 Migration

如果發現 Migration 有錯誤,需要回滾,執行:

php artisan migrate:rollback

這會回滾最近一次的 Migration。

2. 修改 Migration

在開發階段,你可能需要修改已經建立的 Migration。常見的做法是:

  1. 回滾 Migration:
php artisan migrate:rollback
  1. 修改 Migration 檔案。
  2. 重新執行 Migration:
php artisan migrate

小提醒:在正式環境中,不建議直接修改已經執行的 Migration,而是應該建立新的 Migration 來修改資料表結構。

六、填充測試資料(Seeder)

為了測試,我們可以使用 Laravel 的 Seeder 來填充一些測試資料。

1. 建立 Seeder

執行指令:

php artisan make:seeder UsersTableSeeder

這會在 database/seeders 目錄下生成一個 UsersTableSeeder.php 檔案。

2. 編寫 Seeder

打開 UsersTableSeeder.php,填寫以下內容:

public function run()
{
    User::factory()->create([ 
        'username' => 'testuser',
        'email' => 'test@example.com',
        'password' => bcrypt('password'),
        'created_at' => now(),
        'updated_at' => now(),
    ]);
}

3. 執行 Seeder

在 DatabaseSeeder.php 中,呼叫剛剛建立的 Seeder:

public function run()
{
    $this->call(UsersTableSeeder::class);
}

執行 Seeder:

php artisan db:seed

這樣,我們就為 users 表填充了一筆測試資料。

小結

今天,我們深入了解了 Laravel 的 Migration,並實際建立了我們之前設計的資料表。透過 Migration,我們可以:

  • 方便地管理資料庫結構:所有的資料表定義都在程式碼中,可追溯、可版本控制。
  • 團隊協作更順暢:團隊成員之間的資料庫變更可以透過版本控制系統同步。
  • 自動化部署:在部署到不同環境時,只需執行 Migration,就能確保資料庫結構一致。

個人經驗分享:一開始接觸 Migration 時,可能會覺得多此一舉,但當你經歷過手動修改資料庫帶來的痛苦後,你就會發現 Migration 是多麼的可靠和方便。

Next

在完成資料表的建立後,接下來我們將:建立模型(Models),與資料表進行互動!


上一篇
D8 - 深入設計個人財務管理系統的資料庫結構
下一篇
D10 - 深入 Laravel 的 Model,與資料庫暢快互動
系列文
我獨自開發 - 30天進化之路,掌握 Laravel + Nuxt30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言