Skip to content

資料庫:Migrations

簡介

Migrations 就像是資料庫的版本控制,讓您的團隊能夠定義並分享應用程式的資料庫結構定義。如果您曾經需要告訴同事在從版本控制拉取您的變更後,手動在其本地端資料庫結構中新增一個欄位,那麼您就遇到了資料庫 Migrations 所要解決的問題。

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 選項。給定的路徑應該相對於應用程式的根目錄。

📌 備註

Migration 的 stub 可以使用 stub 發佈進行自訂。

壓縮 Migrations

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

shell
php artisan schema:dump

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

當您執行此指令時,Laravel 會將一個「schema」檔案寫入應用程式的 database/schema 目錄中。Schema 檔案的名稱將對應於資料庫連線。現在,當您嘗試執行資料庫 Migration 且沒有執行過其他 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

若您想在不實際執行的情況下查看 Migrations 將執行的 SQL 陳述句,可以在 migrate 指令中使用 --pretend 旗標:

shell
php artisan migrate --pretend

隔離 Migration 執行

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

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

shell
php artisan migrate --isolated

⚠️ 警告

To utilize this feature, your application must be using the memcached, redis, dynamodb, database, file, or array cache driver as your application's default cache driver. In addition, all servers must be communicating with the same central cache server.

強制在正式環境中執行 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 資料表中的批次值。例如,以下指令將回滾批次編號為 3 的所有 Migrations:

shell
php artisan migrate:rollback --batch=3

若您想在不實際執行的情況下查看 Migrations 將執行的 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

⚠️ 警告

The migrate:fresh command will drop all database tables regardless of their prefix. This command should be used with caution when developing on a database that is shared with other applications.

資料表

建立資料表

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

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 屬性可用於指定所建立資料表的字元集和定序:

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

    // ...
});

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

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

    // ...
});

如果您想為資料庫資料表新增「註解 (comment)」,可以在資料表執行個體上呼叫 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');

重新命名包含外鍵的資料表

在重新命名資料表之前,您應該確認資料表上的任何外鍵約束在您的 Migration 檔案中都有明確的名稱,而不是讓 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 構建器 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 (含時區) 等價欄位,並具有選用的秒數精度 (Fractional Seconds Precision):

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

dateTime()

dateTime 方法會建立一個 DATETIME 等價欄位,並具有選用的秒數精度 (Fractional Seconds Precision):

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 方法會為指定的模型類別新增一個 {column}_id 等價欄位。欄位型別將根據模型主鍵型別為 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 資料庫,您必須先安裝 PostGIS 擴充功能,然後才能使用 geography 方法。

geometry()

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

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

📌 備註

對空間型別的支援取決於您的資料庫驅動程式。請參閱您的資料庫文件。如果您的應用程式使用的是 PostgreSQL 資料庫,您必須先安裝 PostGIS 擴充功能,然後才能使用 geometry 方法。

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}_type VARCHAR 等價欄位。{column}_id 的欄位型別將根據模型主鍵型別為 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 方法會建立一個可為 null 的 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 方法會新增一個可為 null 的 deleted_at TIMESTAMP (含時區) 等價欄位,並具有選用的秒數精度。此欄位旨在儲存 Eloquent 的「軟刪除」功能所需的 deleted_at 時間戳記:

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

softDeletes()

softDeletes 方法會新增一個可為 null 的 deleted_at TIMESTAMP 等價欄位,並具有選用的秒數精度。此欄位旨在儲存 Eloquent 的「軟刪除」功能所需的 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_at TIMESTAMP (含時區) 等價欄位,並具有選用的秒數精度:

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

timestamps()

timestamps 方法會建立 created_atupdated_at TIMESTAMP 等價欄位,並具有選用的秒數精度:

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);

year()

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

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

欄位修改器

除了上面列出的欄位型別外,在向資料表新增欄位時,還有幾個欄位「修改器 (modifiers)」可以使用。例如,若要讓欄位可以為「可空 (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)建立一個具有指定序列選項的識別欄位 (PostgreSQL)。
->always()定義序列值優先於識別欄位的輸入值 (PostgreSQL)。

預設運算式

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');
});

即時欄位操作

使用 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 建構器提供的 renameColumn 方法:

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

刪除欄位

若要刪除欄位,您可以使用 Schema 建構器上的 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 支援的每種索引型別的方法。每個索引方法都接受一個可選的第二個參數來指定索引的名稱。如果省略,名稱將根據用於索引的資料表和欄位名稱以及索引型別衍生而來。下表說明了每個可用的索引方法:

指令描述
$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 會根據資料表名稱、索引欄位名稱和索引型別自動分配索引名稱。以下是一些範例:

指令描述
$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 資料表上定義一個 user_id 欄位,它參照了 users 資料表上的 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 傾印檔案已載入。