Skip to content

資料庫:Migrations

簡介

Migrations 就像是資料庫的版本控制,讓您的團隊可以定義並共享應用程式的資料庫 Schema 定義。如果您曾經需要告訴隊友在從版本控制拉取您的變更後,手動向其本地資料庫 Schema 新增欄位,那麼您就遇到了 Migration 所要解決的問題。

Laravel Schema Facade 提供了不分資料庫類型的支援,可用於在所有 Laravel 支援的資料庫系統中建立與操作資料表。通常,Migrations 會使用此 Facade 來建立與修改資料表及欄位。

產生 Migrations

您可以使用 make:migration Artisan 指令來產生資料庫 Migration。新的 Migration 將會被放置在您的 database/migrations 目錄中。每個 Migration 檔名都包含一個時間戳記,讓 Laravel 能夠判斷 Migrations 的順序:

shell
php artisan make:migration create_flights_table

Laravel 會使用 Migration 的名稱來嘗試猜測資料表的名稱,以及該 Migration 是否將建立新資料表。如果 Laravel 能夠從 Migration 名稱中判定資料表名稱,Laravel 會在產生的 Migration 檔案中預填指定的資料表。否則,您只需手動在 Migration 檔案中指定資料表即可。

如果您想為產生的 Migration 指定自訂路徑,可以在執行 make:migration 指令時使用 --path 選項。給定的路徑應相對於應用程式的基礎路徑。

📌 備註

可以使用 Stub 發佈 來自訂 Migration Stub。

壓縮 Migrations

隨著您開發應用程式,隨著時間推移,您可能會累積越來越多的 Migrations。這可能導致您的 database/migrations 目錄變得臃腫,甚至可能有數百個 Migrations。如果您願意,可以將您的 Migrations 「壓縮 (Squash)」成單個 SQL 檔案。若要開始,請執行 schema:dump 指令:

shell
php artisan schema:dump

# Dump the current database schema and prune all existing migrations...
php artisan schema:dump --prune

當您執行此指令時,Laravel 會在應用程式的 database/schema 目錄中寫入一個「Schema」檔案。Schema 檔案的名稱將對應於資料庫連線。現在,當您嘗試遷移資料庫且尚未執行其他 Migrations 時,Laravel 將首先執行您正在使用的資料庫連線之 Schema 檔案中的 SQL 陳述式。在執行完 Schema 檔案的 SQL 陳述式後,Laravel 將執行任何不屬於 Schema Dump 的剩餘 Migrations。

如果您的應用程式測試使用與本地開發時不同的資料庫連線,您應確保已使用該資料庫連線 Dump 出 Schema 檔案,以便您的測試能夠構建資料庫。您可能希望在 Dump 平時本地開發使用的資料庫連線之後執行此操作:

shell
php artisan schema:dump
php artisan schema:dump --database=testing --prune

您應該將資料庫 Schema 檔案提交到版本控制中,以便團隊中的其他新開發人員可以快速建立應用程式的初始資料庫結構。

⚠️ 警告

Migration 壓縮功能僅適用於 MariaDB、MySQL、PostgreSQL 和 SQLite 資料庫,並利用資料庫的命令列用戶端進行操作。

Migration 結構

一個 Migration 類別包含兩個方法:updownup 方法用於向資料庫新增資料表、欄位或索引,而 down 方法則應該還原 up 方法所執行的操作。

在這兩個方法中,您都可以使用 Laravel Schema 構建器來直觀地建立與修改資料表。若要了解 Schema 構建器上所有可用的方法,請查看其文件。例如,以下 Migration 建立了 flights 資料表:

php
<?php

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

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

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

設定 Migration 連線

如果您的 Migration 將與應用程式預設資料庫連線以外的資料庫連線進行互動,您應該設定 Migration 的 $connection 屬性:

php
/**
 * The database connection that should be used by the migration.
 *
 * @var string
 */
protected $connection = 'pgsql';

/**
 * Run the migrations.
 */
public function up(): void
{
    // ...
}

跳過 Migrations

有時候,Migration 可能是為了支援尚未啟用的功能,而您現在還不想執行它。在這種情況下,您可以在 Migration 中定義一個 shouldRun 方法。如果 shouldRun 方法回傳 false,該 Migration 將被跳過:

php
use App\Models\Flight;
use Laravel\Pennant\Feature;

/**
 * Determine if this migration should run.
 */
public function shouldRun(): bool
{
    return Feature::active(Flight::class);
}

執行 Migrations

要執行所有待處理的 Migrations,請執行 migrate Artisan 指令:

shell
php artisan migrate

如果您想查看哪些 Migrations 已經執行,哪些仍在等待執行,可以使用 migrate:status Artisan 指令:

shell
php artisan migrate:status

如果您在 migrate 指令中加上 --step 選項,則該指令會將每個 Migration 作為獨立的批次執行,讓您稍後可以使用 migrate:rollback 指令還原個別的 Migration:

shell
php artisan migrate --step

如果您想在不實際執行的情況下查看 Migrations 將會執行的 SQL 語句,可以在 migrate 指令中加上 --pretend 旗標:

shell
php artisan migrate --pretend

隔離 Migration 執行

如果您將應用程式部署到多台伺服器,並將執行 Migrations 作為部署程序的一部分,您可能不希望兩台伺服器同時嘗試遷移資料庫。為了避免這種情況,您可以在呼叫 migrate 指令時使用 isolated 選項。

當提供 isolated 選項時,Laravel 在嘗試執行 Migrations 之前,會使用應用程式的快取驅動程式取得一個原子鎖 (Atomic Lock)。在持有該鎖定期間,所有其他執行 migrate 指令的嘗試都不會執行;然而,該指令仍會以成功的結束狀態碼結束:

shell
php artisan migrate --isolated

⚠️ 警告

要使用此功能,您的應用程式必須使用 memcachedredisdynamodbdatabasefilearray 快取驅動程式作為應用程式的預設快取驅動程式。此外,所有伺服器必須與同一個中央快取伺服器進行通訊。

在正式環境強制執行 Migrations

某些 Migration 操作具有破壞性,這意味著它們可能會導致數據丟失。為了防止您在正式環境資料庫執行這些指令,在執行指令之前系統會提示您進行確認。若要強制指令在不提示的情況下執行,請使用 --force 旗標:

shell
php artisan migrate --force

還原 Migrations

要還原最後一次的 Migration 操作,您可以使用 rollback Artisan 指令。此指令會還原最後一個「批次 (Batch)」的 Migrations,其中可能包含多個 Migration 檔案:

shell
php artisan migrate:rollback

您可以透過在 rollback 指令中提供 step 選項來還原有限數量的 Migrations。例如,以下指令將還原最後五個 Migrations:

shell
php artisan migrate:rollback --step=5

您可以透過在 rollback 指令中提供 batch 選項來還原特定的「批次 (Batch)」Migrations,其中 batch 選項對應於應用程式 migrations 資料表中的批次值。例如,以下指令將還原批次三中的所有 Migrations:

shell
php artisan migrate:rollback --batch=3

如果您想在不實際執行的情況下查看還原將會執行的 SQL 語句,可以在 migrate:rollback 指令中加上 --pretend 旗標:

shell
php artisan migrate:rollback --pretend

migrate:reset 指令將還原應用程式所有的 Migrations:

shell
php artisan migrate:reset

使用單一指令還原並遷移

migrate:refresh 指令會還原您所有的 Migrations,然後執行 migrate 指令。此指令可有效地重新建立您的整個資料庫:

shell
php artisan migrate:refresh

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

您可以透過在 refresh 指令中提供 step 選項來還原並重新遷移有限數量的 Migrations。例如,以下指令將還原並重新遷移最後五個 Migrations:

shell
php artisan migrate:refresh --step=5

刪除所有資料表並遷移

migrate:fresh 指令會刪除資料庫中的所有資料表,然後執行 migrate 指令:

shell
php artisan migrate:fresh

php artisan migrate:fresh --seed

預設情況下,migrate:fresh 指令僅會刪除預設資料庫連接中的資料表。但是,您可以使用 --database 選項來指定應遷移的資料庫連接。資料庫連接名稱應對應於應用程式 database 設定檔中定義的連接:

shell
php artisan migrate:fresh --database=admin

⚠️ 警告

migrate:fresh 指令將刪除所有資料庫資料表,無論其前綴為何。在與其他應用程式共用的資料庫上進行開發時,應謹慎使用此指令。

資料表

建立資料表

若要建立新的資料庫資料表,請使用 Schema Facade 的 create 方法。create 方法接受兩個參數:第一個是資料表的名稱,第二個則是接收一個可以用來定義新資料表的 Blueprint 物件的閉包 (Closure):

php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

Schema::create('users', function (Blueprint $table) {
    $table->id();
    $table->string('name');
    $table->string('email');
    $table->timestamps();
});

建立資料表時,您可以使用 Schema 建構器的任何 欄位方法 來定義資料表的欄位。

檢查資料表 / 欄位是否存在

您可以使用 hasTablehasColumnhasIndex 方法來檢查資料表、欄位或索引是否存在:

php
if (Schema::hasTable('users')) {
    // The "users" table exists...
}

if (Schema::hasColumn('users', 'email')) {
    // The "users" table exists and has an "email" column...
}

if (Schema::hasIndex('users', ['email'], 'unique')) {
    // The "users" table exists and has a unique index on the "email" column...
}

資料庫連線與資料表選項

如果您想在非應用程式預設連線的資料庫連線執行 Schema 操作,請使用 connection 方法:

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

此外,還可以使用其他一些屬性和方法來定義資料表建立的其他面向。當使用 MariaDB 或 MySQL 時,可以使用 engine 屬性來指定資料表的儲存引擎:

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

    // ...
});

當使用 MariaDB 或 MySQL 時,可以使用 charsetcollation 屬性來指定所建立資料表的字元集 (Character Set) 和校對規則 (Collation):

php
Schema::create('users', function (Blueprint $table) {
    $table->charset('utf8mb4');
    $table->collation('utf8mb4_unicode_ci');

    // ...
});

temporary 方法可用於表示該資料表應為「暫時性」資料表。暫時性資料表僅對當前連線的資料庫工作階段可見,並在連線關閉時自動刪除:

php
Schema::create('calculations', function (Blueprint $table) {
    $table->temporary();

    // ...
});

如果您想為資料庫資料表加入「註解」,可以在資料表執行個體上呼叫 comment 方法。目前僅 MariaDB、MySQL 和 PostgreSQL 支援資料表註解:

php
Schema::create('calculations', function (Blueprint $table) {
    $table->comment('Business calculations');

    // ...
});

更新資料表

Schema Facade 的 table 方法可用於更新現有的資料表。與 create 方法類似,table 方法接受兩個參數:資料表名稱和接收一個 Blueprint 執行個體的閉包,您可以使用該執行個體為資料表新增欄位或索引:

php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

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

重新命名 / 刪除資料表

若要重新命名現有的資料庫資料表,請使用 rename 方法:

php
use Illuminate\Support\Facades\Schema;

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

若要刪除現有的資料表,您可以使用 dropdropIfExists 方法:

php
Schema::drop('users');

Schema::dropIfExists('users');

重新命名具有外鍵的資料表

在重新命名資料表之前,您應該確認該資料表上的任何外鍵約束在您的 Migrations 檔案中都有明確的名稱,而不是讓 Laravel 指定一個基於慣例的名稱。否則,外鍵約束名稱將仍指向舊的資料表名稱。

欄位

建立欄位

Schema facade 上的 table 方法可用於更新現有的資料表。如同 create 方法,table 方法接受兩個參數:資料表的名稱,以及一個接收 Illuminate\Database\Schema\Blueprint 實例的閉包,您可以使用該實例向資料表新增欄位:

php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

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

可用的欄位型態

Schema Builder 的 Blueprint 提供了多種方法,對應到您可以新增至資料庫資料表的各種欄位型態。下表列出了所有可用的方法:

布林型態

字串與文字型態

數值型態

日期與時間型態

二進制型態

物件與 JSON 型態

UUID 與 ULID 型態

空間型態

關聯型態

特殊型態

bigIncrements()

bigIncrements 方法會建立一個自動遞增的 UNSIGNED BIGINT (主鍵) 等效欄位:

php
$table->bigIncrements('id');

bigInteger()

bigInteger 方法會建立一個 BIGINT 等效欄位:

php
$table->bigInteger('votes');

binary()

binary 方法會建立一個 BLOB 等效欄位:

php
$table->binary('photo');

當使用 MySQL、MariaDB 或 SQL Server 時,您可以傳遞 lengthfixed 參數來建立 VARBINARYBINARY 等效欄位:

php
$table->binary('data', length: 16); // VARBINARY(16)

$table->binary('data', length: 16, fixed: true); // BINARY(16)

boolean()

boolean 方法會建立一個 BOOLEAN 等效欄位:

php
$table->boolean('confirmed');

char()

char 方法會建立一個具有指定長度的 CHAR 等效欄位:

php
$table->char('name', length: 100);

dateTimeTz()

dateTimeTz 方法會建立一個 DATETIME (含時區) 等效欄位,並帶有選用的秒數小數部分精度:

php
$table->dateTimeTz('created_at', precision: 0);

dateTime()

dateTime 方法會建立一個 DATETIME 等效欄位,並帶有選用的秒數小數部分精度:

php
$table->dateTime('created_at', precision: 0);

date()

date 方法會建立一個 DATE 等效欄位:

php
$table->date('created_at');

decimal()

decimal 方法會建立一個具備指定精度 (總位數) 與純小數位數 (小數位數) 的 DECIMAL 等效欄位:

php
$table->decimal('amount', total: 8, places: 2);

double()

double 方法會建立一個 DOUBLE 等效欄位:

php
$table->double('amount');

enum()

enum 方法會建立一個具備指定有效值列表的 ENUM 等效欄位:

php
$table->enum('difficulty', ['easy', 'hard']);

當然,您也可以使用 Enum::cases() 方法,而不是手動定義允許值的陣列:

php
use App\Enums\Difficulty;

$table->enum('difficulty', Difficulty::cases());

float()

float 方法會建立一個具備指定精度的 FLOAT 等效欄位:

php
$table->float('amount', precision: 53);

foreignId()

foreignId 方法會建立一個 UNSIGNED BIGINT 等效欄位:

php
$table->foreignId('user_id');

foreignIdFor()

foreignIdFor 方法會為指定的 Model 類別建立一個 {column}_id 等效欄位。欄位型態會根據 Model 的主鍵型態為 UNSIGNED BIGINTCHAR(36)CHAR(26)

php
$table->foreignIdFor(User::class);

foreignUlid()

foreignUlid 方法會建立一個 ULID 等效欄位:

php
$table->foreignUlid('user_id');

foreignUuid()

foreignUuid 方法會建立一個 UUID 等效欄位:

php
$table->foreignUuid('user_id');

geography()

geography 方法會建立一個具備指定空間型態與 SRID (空間參照系統識別碼) 的 GEOGRAPHY 等效欄位:

php
$table->geography('coordinates', subtype: 'point', srid: 4326);

📌 備註

支援空間型態取決於您的資料庫驅動程式。請參閱您的資料庫說明文件。若您的應用程式使用 PostgreSQL 資料庫,在使用 geography 方法之前,必須先安裝 PostGIS 擴充功能。

geometry()

geometry 方法會建立一個具備指定空間型態與 SRID (空間參照系統識別碼) 的 GEOMETRY 等效欄位:

php
$table->geometry('positions', subtype: 'point', srid: 0);

📌 備註

支援空間型態取決於您的資料庫驅動程式。請參閱您的資料庫說明文件。若您的應用程式使用 PostgreSQL 資料庫,在使用 geometry 方法之前,必須先安裝 PostGIS 擴充功能。

id()

id 方法是 bigIncrements 方法的別名。預設情況下,該方法會建立一個 id 欄位;然而,如果您想為欄位指定不同的名稱,可以傳遞一個欄位名稱:

php
$table->id();

increments()

increments 方法會建立一個自動遞增的 UNSIGNED INTEGER 等效欄位作為主鍵:

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

integer()

integer 方法會建立一個 INTEGER 等效欄位:

php
$table->integer('votes');

ipAddress()

ipAddress 方法會建立一個 VARCHAR 等效欄位:

php
$table->ipAddress('visitor');

使用 PostgreSQL 時,會建立一個 INET 欄位。

json()

json 方法會建立一個 JSON 等效欄位:

php
$table->json('options');

使用 SQLite 時,會建立一個 TEXT 欄位。

jsonb()

jsonb 方法會建立一個 JSONB 等效欄位:

php
$table->jsonb('options');

使用 SQLite 時,會建立一個 TEXT 欄位。

longText()

longText 方法會建立一個 LONGTEXT 等效欄位:

php
$table->longText('description');

當使用 MySQL 或 MariaDB 時,您可以對欄位套用 binary 字元集,以建立一個 LONGBLOB 等效欄位:

php
$table->longText('data')->charset('binary'); // LONGBLOB

macAddress()

macAddress 方法建立一個用來存放 MAC 位址的欄位。某些資料庫系統(如 PostgreSQL)有專門針對此類資料的欄位型態。其他資料庫系統則會使用字串等效欄位:

php
$table->macAddress('device');

mediumIncrements()

mediumIncrements 方法會建立一個自動遞增的 UNSIGNED MEDIUMINT 等效欄位作為主鍵:

php
$table->mediumIncrements('id');

mediumInteger()

mediumInteger 方法會建立一個 MEDIUMINT 等效欄位:

php
$table->mediumInteger('votes');

mediumText()

mediumText 方法會建立一個 MEDIUMTEXT 等效欄位:

php
$table->mediumText('description');

當使用 MySQL 或 MariaDB 時,您可以對欄位套用 binary 字元集,以建立一個 MEDIUMBLOB 等效欄位:

php
$table->mediumText('data')->charset('binary'); // MEDIUMBLOB

morphs()

morphs 方法是一個便利的方法,它會新增一個 {column}_id 等效欄位與一個 {column}_typeVARCHAR 等效欄位。{column}_id 的欄位型態會根據 Model 的主鍵型態為 UNSIGNED BIGINTCHAR(36)CHAR(26)

此方法旨在用於定義多型 Eloquent 關聯 所需的欄位。在以下範例中,將會建立 taggable_idtaggable_type 欄位:

php
$table->morphs('taggable');

nullableMorphs()

此方法類似於 morphs 方法;但是建立的欄位將會是「可為空 (nullable)」的:

php
$table->nullableMorphs('taggable');

nullableUlidMorphs()

此方法類似於 ulidMorphs 方法;但是建立的欄位將會是「可為空 (nullable)」的:

php
$table->nullableUlidMorphs('taggable');

nullableUuidMorphs()

此方法類似於 uuidMorphs 方法;但是建立的欄位將會是「可為空 (nullable)」的:

php
$table->nullableUuidMorphs('taggable');

rememberToken()

rememberToken 方法會建立一個可為空的 VARCHAR(100) 等效欄位,用於儲存目前的「記住我」認證權杖 (Authentication Token)

php
$table->rememberToken();

set()

set 方法會建立一個具備指定有效值列表的 SET 等效欄位:

php
$table->set('flavors', ['strawberry', 'vanilla']);

smallIncrements()

smallIncrements 方法會建立一個自動遞增的 UNSIGNED SMALLINT 等效欄位作為主鍵:

php
$table->smallIncrements('id');

smallInteger()

smallInteger 方法會建立一個 SMALLINT 等效欄位:

php
$table->smallInteger('votes');

softDeletesTz()

softDeletesTz 方法會新增一個可為空的 deleted_at TIMESTAMP (含時區) 等效欄位,並帶有選用的秒數小數部分精度。此欄位用於儲存 Eloquent「軟刪除 (Soft Delete)」功能所需的 deleted_at 時間戳記:

php
$table->softDeletesTz('deleted_at', precision: 0);

softDeletes()

softDeletes 方法會新增一個可為空的 deleted_at TIMESTAMP 等效欄位,並帶有選用的秒數小數部分精度。此欄位用於儲存 Eloquent「軟刪除 (Soft Delete)」功能所需的 deleted_at 時間戳記:

php
$table->softDeletes('deleted_at', precision: 0);

string()

string 方法會建立一個指定長度的 VARCHAR 等效欄位:

php
$table->string('name', length: 100);

text()

text 方法會建立一個 TEXT 等效欄位:

php
$table->text('description');

當使用 MySQL 或 MariaDB 時,您可以對欄位套用 binary 字元集,以建立一個 BLOB 等效欄位:

php
$table->text('data')->charset('binary'); // BLOB

timeTz()

timeTz 方法會建立一個 TIME (含時區) 等效欄位,並帶有選用的秒數小數部分精度:

php
$table->timeTz('sunrise', precision: 0);

time()

time 方法會建立一個 TIME 等效欄位,並帶有選用的秒數小數部分精度:

php
$table->time('sunrise', precision: 0);

timestampTz()

timestampTz 方法會建立一個 TIMESTAMP (含時區) 等效欄位,並帶有選用的秒數小數部分精度:

php
$table->timestampTz('added_at', precision: 0);

timestamp()

timestamp 方法會建立一個 TIMESTAMP 等效欄位,並帶有選用的秒數小數部分精度:

php
$table->timestamp('added_at', precision: 0);

timestampsTz()

timestampsTz 方法會建立 created_atupdated_atTIMESTAMP (含時區) 等效欄位,並帶有選用的秒數小數部分精度:

php
$table->timestampsTz(precision: 0);

timestamps()

timestamps 方法會建立 created_atupdated_atTIMESTAMP 等效欄位,並帶有選用的秒數小數部分精度:

php
$table->timestamps(precision: 0);

tinyIncrements()

tinyIncrements 方法會建立一個自動遞增的 UNSIGNED TINYINT 等效欄位作為主鍵:

php
$table->tinyIncrements('id');

tinyInteger()

tinyInteger 方法會建立一個 TINYINT 等效欄位:

php
$table->tinyInteger('votes');

tinyText()

tinyText 方法會建立一個 TINYTEXT 等效欄位:

php
$table->tinyText('notes');

當使用 MySQL 或 MariaDB 時,您可以對欄位套用 binary 字元集,以建立一個 TINYBLOB 等效欄位:

php
$table->tinyText('data')->charset('binary'); // TINYBLOB

unsignedBigInteger()

unsignedBigInteger 方法會建立一個 UNSIGNED BIGINT 等效欄位:

php
$table->unsignedBigInteger('votes');

unsignedInteger()

unsignedInteger 方法會建立一個 UNSIGNED INTEGER 等效欄位:

php
$table->unsignedInteger('votes');

unsignedMediumInteger()

unsignedMediumInteger 方法會建立一個 UNSIGNED MEDIUMINT 等效欄位:

php
$table->unsignedMediumInteger('votes');

unsignedSmallInteger()

unsignedSmallInteger 方法會建立一個 UNSIGNED SMALLINT 等效欄位:

php
$table->unsignedSmallInteger('votes');

unsignedTinyInteger()

unsignedTinyInteger 方法會建立一個 UNSIGNED TINYINT 等效欄位:

php
$table->unsignedTinyInteger('votes');

ulidMorphs()

ulidMorphs 方法是一個便利的方法,會新增一個 {column}_id CHAR(26) 等效欄位與一個 {column}_type VARCHAR 等效欄位。

此方法旨在用於定義使用 ULID 識別碼的多型 Eloquent 關聯 所需的欄位。在以下範例中,將會建立 taggable_idtaggable_type 欄位:

php
$table->ulidMorphs('taggable');

uuidMorphs()

uuidMorphs 方法是一個便利的方法,會新增一個 {column}_id CHAR(36) 等效欄位與一個 {column}_type VARCHAR 等效欄位。

此方法旨在用於定義使用 UUID 識別碼的 多型 Eloquent 關聯 所需的欄位。在以下範例中,將會建立 taggable_idtaggable_type 欄位:

php
$table->uuidMorphs('taggable');

ulid()

ulid 方法會建立一個 ULID 等效欄位:

php
$table->ulid('id');

uuid()

uuid 方法會建立一個 UUID 等效欄位:

php
$table->uuid('id');

vector()

vector 方法會建立一個 vector 等效欄位:

php
$table->vector('embedding', dimensions: 100);

當使用 PostgreSQL 時,必須在建立 vector 欄位之前載入 pgvector 擴充功能:

php
Schema::ensureVectorExtensionExists();

year()

year 方法會建立一個 YEAR 等效欄位:

php
$table->year('birth_year');

欄位修飾詞

除了上方列出的欄位型態外,在將欄位新增到資料庫資料表時,還有多種欄位「修飾詞」可以使用。例如,若要讓欄位為「可為空 (nullable)」,可以使用 nullable 方法:

php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

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

下表包含所有可用的欄位修飾詞。此列表不包含索引修飾詞

修飾詞描述
->after('column')將欄位放在另一個欄位「之後」(MariaDB / MySQL)。
->autoIncrement()INTEGER 欄位設定為自動遞增 (主鍵)。
->charset('utf8mb4')為欄位指定字元集 (MariaDB / MySQL)。
->collation('utf8mb4_unicode_ci')為欄位指定排序規則。
->comment('my comment')為欄位新增註解 (MariaDB / MySQL / PostgreSQL)。
->default($value)為欄位指定「預設」值。
->first()將欄位放在資料表中的「第一個」(MariaDB / MySQL)。
->from($integer)設定自動遞增欄位的起始值 (MariaDB / MySQL / PostgreSQL)。
->instant()使用即時 (instant) 操作新增或修改欄位 (MySQL)。
->invisible()讓欄位對 SELECT * 查詢「不可見」(MariaDB / MySQL)。
->lock($mode)為欄位操作指定鎖定模式 (MySQL)。
->nullable($value = true)允許將 NULL 值插入該欄位。
->storedAs($expression)建立一個儲存型產生的欄位 (stored generated column) (MariaDB / MySQL / PostgreSQL / SQLite)。
->unsigned()INTEGER 欄位設定為 UNSIGNED (MariaDB / MySQL)。
->useCurrent()設定 TIMESTAMP 欄位使用 CURRENT_TIMESTAMP 作為預設值。
->useCurrentOnUpdate()設定 TIMESTAMP 欄位在紀錄更新時使用 CURRENT_TIMESTAMP (MariaDB / MySQL)。
->virtualAs($expression)建立一個虛擬型產生的欄位 (virtual generated column) (MariaDB / MySQL / SQLite)。
->generatedAs($expression)建立一個具有指定序列選項的識別欄位 (identity column) (PostgreSQL)。
->always()定義識別欄位的序列值優於輸入值的優先權 (PostgreSQL)。

預設表達式 (Default Expressions)

default 修飾詞接受一個值或 Illuminate\Database\Query\Expression 實例。使用 Expression 實例將防止 Laravel 將該值加上引號,並允許您使用資料庫特定的函數。這在需要為 JSON 欄位指派預設值時特別有用:

php
<?php

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

return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::create('flights', function (Blueprint $table) {
            $table->id();
            $table->json('movies')->default(new Expression('(JSON_ARRAY())'));
            $table->timestamps();
        });
    }
};

⚠️ 警告

預設表達式的支援取決於您的資料庫驅動程式、資料庫版本以及欄位型態。請參閱您的資料庫文件。

欄位順序

當使用 MariaDB 或 MySQL 資料庫時,可以使用 after 方法在 Schema 中現有的欄位之後新增欄位:

php
$table->after('password', function (Blueprint $table) {
    $table->string('address_line1');
    $table->string('address_line2');
    $table->string('city');
});

即時欄位操作 (Instant Column Operations)

當使用 MySQL 時,您可以在欄位定義上串接 instant 修飾詞,以表示該欄位應使用 MySQL 的「即時 (instant)」演算法來新增或修改。此演算法允許在不重新建構完整資料表的情況下執行某些 Schema 變更,使其幾乎是瞬時完成,無論資料表大小如何:

php
$table->string('name')->nullable()->instant();

即時新增欄位只能將欄位附加到資料表的末尾,因此 instant 修飾詞不能與 afterfirst 修飾詞結合使用。此外,該演算法並不支援所有的欄位型態或操作。如果請求的操作不相容,MySQL 將會報錯。

請參閱 MySQL 官方文件以確定哪些操作與即時欄位修改相容。

DDL 鎖定

當使用 MySQL 時,您可以在欄位、索引或外鍵定義上串接 lock 修飾詞,以控制 Schema 操作期間的資料表鎖定。MySQL 支援多種鎖定模式:none 允許並行讀寫,shared 允許並行讀取但阻擋寫入,exclusive 阻擋所有並行存取,而 default 則由 MySQL 選擇最適合的模式:

php
$table->string('name')->lock('none');

$table->index('email')->lock('shared');

如果請求的鎖定模式與操作不相容,MySQL 將會報錯。lock 修飾詞可以與 instant 修飾詞結合使用,以進一步優化 Schema 變更:

php
$table->string('name')->instant()->lock('none');

修改欄位

change 方法允許您修改現有欄位的型態與屬性。例如,您可能希望增加 string 欄位的大小。為了示範 change 方法,讓我們將 name 欄位的大小從 25 增加到 50。要達成此目的,我們只需定義欄位的新狀態,然後呼叫 change 方法:

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

修改欄位時,您必須明確包含所有想要保留在欄位定義上的修飾詞——任何缺失的屬性都將被捨棄。例如,為了保留 unsigneddefaultcomment 屬性,您在變更欄位時必須明確呼叫每個修飾詞:

php
Schema::table('users', function (Blueprint $table) {
    $table->integer('votes')->unsigned()->default(1)->comment('my comment')->change();
});

change 方法不會變更欄位的索引。因此,在修改欄位時,您可以使用索引修飾詞來明確地新增或刪除索引:

php
// Add an index...
$table->bigIncrements('id')->primary()->change();

// Drop an index...
$table->char('postal_code', 10)->unique(false)->change();

重新命名欄位

若要重新命名欄位,你可以使用 Schema builder 提供的 renameColumn 方法:

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

刪除欄位

若要刪除欄位,你可以在 Schema builder 使用 dropColumn 方法:

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

你可以透過向 dropColumn 方法傳遞一個包含欄位名稱的陣列,來從資料表中刪除多個欄位:

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

可用的指令別名

Laravel 提供了幾個與刪除常見欄位類型相關的便利方法。下表說明了其中的每個方法:

指令描述
$table->dropMorphs('morphable');刪除 morphable_idmorphable_type 欄位。
$table->dropRememberToken();刪除 remember_token 欄位。
$table->dropSoftDeletes();刪除 deleted_at 欄位。
$table->dropSoftDeletesTz();dropSoftDeletes() 方法的別名。
$table->dropTimestamps();刪除 created_atupdated_at 欄位。
$table->dropTimestampsTz();dropTimestamps() 方法的別名。

索引

建立索引

Laravel 的 schema 建立器支援多種類型的索引。以下範例建立了一個新的 email 欄位,並指定其值必須是唯一的。為了建立索引,我們可以在欄位定義上串接 unique 方法:

php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

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

或者,您可以在定義欄位後再建立索引。若要執行此操作,您應該在 schema 建立器 blueprint 上呼叫 unique 方法。此方法接受應取得唯一索引的欄位名稱:

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

您甚至可以將欄位陣列傳遞給索引方法,以建立複合 (Compound 或 Composite) 索引:

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

在建立索引時,Laravel 會根據資料表、欄位名稱和索引類型自動產生索引名稱,但您可以將第二個參數傳遞給該方法,以自行指定索引名稱:

php
$table->unique('email', 'unique_email');

可用的索引類型

Laravel 的 schema 建立器 blueprint 類別提供了建立 Laravel 支援的每種索引類型的方法。每個索引方法都接受一個選用的第二個參數來指定索引的名稱。如果省略,名稱將根據用於索引的資料表和欄位名稱以及索引類型來衍生。下表說明了每個可用的索引方法:

CommandDescription
$table->primary('id');新增主鍵。
$table->primary(['id', 'parent_id']);新增複合鍵。
$table->unique('email');新增唯一索引。
$table->index('state');新增索引。
$table->fullText('body');新增全文檢索索引 (MariaDB / MySQL / PostgreSQL)。
$table->fullText('body')->language('english');新增指定語言的全文檢索索引 (PostgreSQL)。
$table->spatialIndex('location');新增空間索引 (除 SQLite 之外)。

線上建立索引

預設情況下,在大型資料表上建立索引可能會鎖定資料表,並在建立索引時阻斷讀取或寫入。使用 PostgreSQL 或 SQL Server 時,您可以在索引定義上串接 online 方法,以便在不鎖定資料表的情況下建立索引,從而讓您的應用程式在索引建立期間繼續讀取和寫入資料:

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

使用 PostgreSQL 時,這會在索引建立語句中加入 CONCURRENTLY 選項。使用 SQL Server 時,這會在索引建立語句中加入 WITH (online = on) 選項。

重新命名索引

若要重新命名索引,您可以使用 schema 建立器 blueprint 提供的 renameIndex 方法。此方法接受目前的索引名稱作為其第一個參數,並將所需的名稱作為其第二個參數:

php
$table->renameIndex('from', 'to')

刪除索引

若要刪除索引,您必須指定索引的名稱。預設情況下,Laravel 會根據資料表名稱、索引欄位的名稱和索引類型自動指派索引名稱。以下是一些範例:

CommandDescription
$table->dropPrimary('users_id_primary');從 "users" 資料表中刪除主鍵。
$table->dropUnique('users_email_unique');從 "users" 資料表中刪除唯一索引。
$table->dropIndex('geo_state_index');從 "geo" 資料表中刪除基本索引。
$table->dropFullText('posts_body_fulltext');從 "posts" 資料表中刪除全文檢索索引。
$table->dropSpatialIndex('geo_location_spatialindex');從 "geo" 資料表中刪除空間索引 (除 SQLite 之外)。

如果您將欄位陣列傳遞給刪除索引的方法,則會根據資料表名稱、欄位和索引類型產生慣例的索引名稱:

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

外鍵約束

Laravel 也支援建立外鍵約束,這用於在資料庫層級強制執行參照完整性。例如,讓我們在 posts 資料表上定義一個參考 users 資料表 id 欄位的 user_id 欄位:

php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

Schema::table('posts', function (Blueprint $table) {
    $table->unsignedBigInteger('user_id');

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

由於此語法相當冗長,Laravel 提供了額外、更簡潔的方法,利用慣例來提供更好的開發者體驗。當使用 foreignId 方法建立欄位時,上述範例可以改寫如下:

php
Schema::table('posts', function (Blueprint $table) {
    $table->foreignId('user_id')->constrained();
});

foreignId 方法會建立一個等同於 UNSIGNED BIGINT 的欄位,而 constrained 方法將使用慣例來判斷所參考的資料表和欄位。如果您的資料表名稱不符合 Laravel 的慣例,您可以手動將其提供給 constrained 方法。此外,也可以指定應分配給生成的索引名稱:

php
Schema::table('posts', function (Blueprint $table) {
    $table->foreignId('user_id')->constrained(
        table: 'users', indexName: 'posts_user_id'
    );
});

您也可以為約束的 "on delete" 與 "on update" 屬性指定所需的動作:

php
$table->foreignId('user_id')
    ->constrained()
    ->onUpdate('cascade')
    ->onDelete('cascade');

這些動作也提供了另一種具表達性的語法:

方法說明
$table->cascadeOnUpdate();更新時應串聯。
$table->restrictOnUpdate();更新時應受限。
$table->nullOnUpdate();更新時應將外鍵值設為 null。
$table->noActionOnUpdate();更新時無動作。
$table->cascadeOnDelete();刪除時應串聯。
$table->restrictOnDelete();刪除時應受限。
$table->nullOnDelete();刪除時應將外鍵值設為 null。
$table->noActionOnDelete();若存在子紀錄則防止刪除。

任何額外的欄位修飾詞都必須在 constrained 方法之前呼叫:

php
$table->foreignId('user_id')
    ->nullable()
    ->constrained();

刪除外鍵

要刪除外鍵,您可以使用 dropForeign 方法,並將要刪除的外鍵約束名稱作為參數傳遞。外鍵約束使用與索引相同的命名慣例。換句話說,外鍵約束名稱是基於資料表名稱和約束中的欄位名稱,後接 "_foreign" 後綴:

php
$table->dropForeign('posts_user_id_foreign');

或者,您可以將包含持有外鍵的欄位名稱的陣列傳遞給 dropForeign 方法。該陣列將使用 Laravel 的約束命名慣例轉換為外鍵約束名稱:

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

切換外鍵約束

您可以使用以下方法在 Migrations 中啟用或停用外鍵約束:

php
Schema::enableForeignKeyConstraints();

Schema::disableForeignKeyConstraints();

Schema::withoutForeignKeyConstraints(function () {
    // Constraints disabled within this closure...
});

⚠️ 警告

SQLite 預設會停用外鍵約束。使用 SQLite 時,請確保在嘗試於 Migrations 中建立外鍵之前,已在資料庫設定中啟用外鍵支援

事件

為了方便起見,每個 Migration 操作都會發送一個 事件。以下所有事件都繼承了 Illuminate\Database\Events\MigrationEvent 基礎類別:

類別描述
Illuminate\Database\Events\MigrationsStarted一批 Migrations 即將執行。
Illuminate\Database\Events\MigrationsEnded一批 Migrations 已執行完畢。
Illuminate\Database\Events\MigrationStarted單個 Migration 即將執行。
Illuminate\Database\Events\MigrationEnded單個 Migration 已執行完畢。
Illuminate\Database\Events\NoPendingMigrationsMigration 指令找不到任何待處理的 Migrations。
Illuminate\Database\Events\SchemaDumped資料庫 Schema 傾印已完成。
Illuminate\Database\Events\SchemaLoaded現有的資料庫 Schema 傾印已載入。