資料庫:Migration

簡介

Migration(移轉)」就像是資料表的版本控制一樣,我們能通過 Migration 來定義並與開發團隊共享專案的資料庫結構定義。讀者是否曾經在從版控拉去更新後,還需要告訴同事要手動新增欄位?資料庫 Migration 就是要解決這樣的問題。

Laravel 的 Schema Facade 提供了一種可建立或修改資料表的功能,該功能不區分資料,可用在所有 Laravel 支援的資料庫系統上。一般來說,Migration 會使用該 Facade 來建立或修改資料庫資料表與欄位。

產生 Migration

我們可以使用 make:migration Artisan 指令 來產生資料庫 Migration。新建立的 Migration 會放在 database/migrations 目錄下。各個 Migration 的檔名都包含了一個時戳,用來讓 Laravel 判斷各個 Migration 的執行順序:

1php artisan make:migration create_flights_table
1php artisan make:migration create_flights_table

Laravel 會使用 Migration 的名稱來嘗試推測資料表的名稱,並嘗試推測該 Migration 是否要建立新資料表。若 Laravel 可判斷檔案名稱,則 Laravel 會預先在產生的 Migration 檔中填入特定的資料表。若無法判斷時,我們只需要在 Migration 檔中手動指定資料表即可。

若想為產生的 Migration 檔指定自訂的路徑,則可在執行 make:migration 指令時使用 --path 選項。給定的路徑應為相對於專案根目錄的相對路徑。

lightbulb

可以安裝 Stub 來自訂 Migration 的 Stub。

壓縮 Migration

在我們持續撰寫專案的同時,我們可能會逐漸累積出越來越多的資料庫 Migration 檔。這樣可能會導致 database/migrations 目錄中包含了數百個 Migration 檔。若有需要的話,我們可以將 Migration 檔「壓縮」進單一 SQL 檔內。要開始壓縮,請執行 schema:dump 指令:

1php artisan schema:dump
2 
3// 傾印目前的資料庫結構,並刪除所有現存的 Migration...
4php artisan schema:dump --prune
1php artisan schema:dump
2 
3// 傾印目前的資料庫結構,並刪除所有現存的 Migration...
4php artisan schema:dump --prune

執行該指令時,Laravel 會將一個「Schema(結構描述)」檔案寫入 database/schema 目錄內。接著,當要移轉資料庫且尚未執行過任何 Migration 時,Laravel 會先執行該 Schema 檔中的 SQL。執行完 Schema 檔內的陳述式後,接著 Laravel 才會執行不在該 Schema 傾印中剩下的 Migration。

請將資料庫 Schema 檔 Commit(簽入) 進版本控制中,好讓團隊中其他的新開發人員可快速建立專案的初始資料庫結構。

exclamation

Migration 壓縮只支援 MySQL、PostgreSQL、SQLite 等資料庫,且會使用資料庫的主控台用戶端。Schema 傾印無法用來復原 In-Memory 的 SQLite 資料庫。

Migration 的架構

Migration 類別中包含了兩個方法:updownup 方法可用來在資料庫中新增新資料表、欄位、索引等;而 down 方法則用來做與 up 方法相反的事。

在這兩個方法中,我們可以使用 Laravel 的 Schema Builder 來以描述性的方法建立與修改資料表。若要瞭解 Schema Builder 中所有可用的方法,請參考 Schema Builder 的說明文件。舉例來說,下列 Migration 會建立一個 flights 資料表:

1<?php
2 
3use Illuminate\Database\Migrations\Migration;
4use Illuminate\Database\Schema\Blueprint;
5use Illuminate\Support\Facades\Schema;
6 
7class CreateFlightsTable extends Migration
8{
9 /**
10 * Run the migrations.
11 *
12 * @return void
13 */
14 public function up()
15 {
16 Schema::create('flights', function (Blueprint $table) {
17 $table->id();
18 $table->string('name');
19 $table->string('airline');
20 $table->timestamps();
21 });
22 }
23 
24 /**
25 * Reverse the migrations.
26 *
27 * @return void
28 */
29 public function down()
30 {
31 Schema::drop('flights');
32 }
33}
1<?php
2 
3use Illuminate\Database\Migrations\Migration;
4use Illuminate\Database\Schema\Blueprint;
5use Illuminate\Support\Facades\Schema;
6 
7class CreateFlightsTable extends Migration
8{
9 /**
10 * Run the migrations.
11 *
12 * @return void
13 */
14 public function up()
15 {
16 Schema::create('flights', function (Blueprint $table) {
17 $table->id();
18 $table->string('name');
19 $table->string('airline');
20 $table->timestamps();
21 });
22 }
23 
24 /**
25 * Reverse the migrations.
26 *
27 * @return void
28 */
29 public function down()
30 {
31 Schema::drop('flights');
32 }
33}

匿名 Migration

在上述的範例中可以看到,Laravel 會自動為所有使用 make:migration 指令所產生的所有 Migration 指定一個類別名稱。不過,若有需要的話,我們可以在 Migration 檔中回傳一個匿名類別。若專案中已經有許多的 Migration,且其中某兩個 Migration 發生的類別名稱衝突時,就很適合使用這個方法:

1<?php
2 
3use Illuminate\Database\Migrations\Migration;
4 
5return new class extends Migration
6{
7 //
8};
1<?php
2 
3use Illuminate\Database\Migrations\Migration;
4 
5return new class extends Migration
6{
7 //
8};

設定 Migration 的連線

若 Migration 會使用與專案預設資料庫連線不同的資料庫連線,則請在 Migration 中設定 $connection 屬性:

1/**
2 * The database connection that should be used by the migration.
3 *
4 * @var string
5 */
6protected $connection = 'pgsql';
7 
8/**
9 * Run the migrations.
10 *
11 * @return void
12 */
13public function up()
14{
15 //
16}
1/**
2 * The database connection that should be used by the migration.
3 *
4 * @var string
5 */
6protected $connection = 'pgsql';
7 
8/**
9 * Run the migrations.
10 *
11 * @return void
12 */
13public function up()
14{
15 //
16}

執行 Migration

若要執行所有尚未執行過的 Migration,請執行 migrate Artisan 指令:

1php artisan migrate
1php artisan migrate

若想檢視目前為止已執行了哪些 Migration,可使用 migrate:status Artisan 指令:

1php artisan migrate:status
1php artisan migrate:status

在正式環境中強制執行 Migration

有些 Migration 中的動作是破壞性的,也就是一些會導致資料消失的動作。為了避免在正式環境資料庫中執行這些破壞性的動作,因此在執行指令時,會出現提示要求確認。若要強制該指令不跳出提示直接執行,請使用 --force 旗標:

1php artisan migrate --force
1php artisan migrate --force

復原 Migration

若要復原最後執行的 Migration 動作,可使用 rollback Artisan 指令。該指令會復原最後「一批」執行的 Migration,其中可能包含多個 Migration 檔:

1php artisan migrate:rollback
1php artisan migrate:rollback

我們也可以提供各一個 step 選項給 rollback 指令,以限制要復原的 Migration 數量。舉例來說,下列指令只會復原最後 5 個 Migration:

1php artisan migrate:rollback --step=5
1php artisan migrate:rollback --step=5

migrate:reset 指令會復原專案中所有的 Migration:

1php artisan migrate:reset
1php artisan migrate:reset

以單一指令來復原並 Migrate

migrate:refresh 指令會將所有的 Migration 都復原回去,並接著執行 migrate 指令。使用該指令,就可以有效率的重建整個資料庫:

1php artisan migrate:refresh
2 
3// 重新整理資料庫,並執行所有的資料庫 Seed...
4php artisan migrate:refresh --seed
1php artisan migrate:refresh
2 
3// 重新整理資料庫,並執行所有的資料庫 Seed...
4php artisan migrate:refresh --seed

我們也可以提供各一個 step 選項給 refresh 指令,以限制要復原並重新 Migrate 的 Migration 數量。舉例來說,下列指令只會復原並重新 Migrate 最後 5 個 Migration:

1php artisan migrate:refresh --step=5
1php artisan migrate:refresh --step=5

刪除所有資料表並 Migrate

migrate:fresh 指令會刪除資料庫中所有資料表,並接著執行 migrate 指令:

1php artisan migrate:fresh
2 
3php artisan migrate:fresh --seed
1php artisan migrate:fresh
2 
3php artisan migrate:fresh --seed
exclamation

不論資料表是否有前置詞(Prefix)migrate:fresh 指令會刪除所有的資料庫資料表。在使用與其他專案共享的資料庫時,若要與本指令搭配使用請務必注意。

資料表

建立資料表

若要建立新的資料庫資料表,請使用 Schema Facade 上的 create 方法。create 方法接受兩個引數:資料表名稱、以及一個接收 Blueprint 物件的閉包。Blueprint 物件可用來定義新資料表:

1use Illuminate\Database\Schema\Blueprint;
2use Illuminate\Support\Facades\Schema;
3 
4Schema::create('users', function (Blueprint $table) {
5 $table->id();
6 $table->string('name');
7 $table->string('email');
8 $table->timestamps();
9});
1use Illuminate\Database\Schema\Blueprint;
2use Illuminate\Support\Facades\Schema;
3 
4Schema::create('users', function (Blueprint $table) {
5 $table->id();
6 $table->string('name');
7 $table->string('email');
8 $table->timestamps();
9});

建立資料表時,我們可以使用任意 Schema Builder 的欄位方法來定義資料表欄位。

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

我們可以使用 hasTablehasColumn 方法來檢查資料表或欄位是否存在:

1if (Schema::hasTable('users')) {
2 // 「users」資料表存在...
3}
4 
5if (Schema::hasColumn('users', 'email')) {
6 // 「users」資料表存在,且包含一個「email」欄位...
7}
1if (Schema::hasTable('users')) {
2 // 「users」資料表存在...
3}
4 
5if (Schema::hasColumn('users', 'email')) {
6 // 「users」資料表存在,且包含一個「email」欄位...
7}

資料庫連線與資料表選項

若要在非專案預設連線的資料庫連線上做 Schema 動作,請使用 connection 方法:

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

此外,還有一些其他的屬性或方法,可用來調整資料表建立中的其他細節。使用 MySQL 時,可使用 engine 屬性來指定資料表的 Storage Engine:

1Schema::create('users', function (Blueprint $table) {
2 $table->engine = 'InnoDB';
3 
4 // ...
5});
1Schema::create('users', function (Blueprint $table) {
2 $table->engine = 'InnoDB';
3 
4 // ...
5});

使用 MySQL 時,charsetcollation 屬性可用來指定建立資料表的 Character Set 與 Collection:

1Schema::create('users', function (Blueprint $table) {
2 $table->charset = 'utf8mb4';
3 $table->collation = 'utf8mb4_unicode_ci';
4 
5 // ...
6});
1Schema::create('users', function (Blueprint $table) {
2 $table->charset = 'utf8mb4';
3 $table->collation = 'utf8mb4_unicode_ci';
4 
5 // ...
6});

temporary 方法可用來表示該資料表是「臨時」資料表。臨時資料表只可在目前連線的資料庫工作階段中使用,且會在連線關閉後自動刪除:

1Schema::create('calculations', function (Blueprint $table) {
2 $table->temporary();
3 
4 // ...
5});
1Schema::create('calculations', function (Blueprint $table) {
2 $table->temporary();
3 
4 // ...
5});

更新資料表

Schema Facade 上的 table 方法可用來更新現有的資料表。與 create 方法類似,table 方法接受兩個因數:資料表名稱,以及一個接收 Blueprint 實體的閉包。使用 Blueprint 實體,即可用來在資料表上新增欄位或索引:

1use Illuminate\Database\Schema\Blueprint;
2use Illuminate\Support\Facades\Schema;
3 
4Schema::table('users', function (Blueprint $table) {
5 $table->integer('votes');
6});
1use Illuminate\Database\Schema\Blueprint;
2use Illuminate\Support\Facades\Schema;
3 
4Schema::table('users', function (Blueprint $table) {
5 $table->integer('votes');
6});

重新命名或刪除資料表

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

1use Illuminate\Support\Facades\Schema;
2 
3Schema::rename($from, $to);
1use Illuminate\Support\Facades\Schema;
2 
3Schema::rename($from, $to);

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

1Schema::drop('users');
2 
3Schema::dropIfExists('users');
1Schema::drop('users');
2 
3Schema::dropIfExists('users');

與外部索引鍵一起重新命名資料表

在重新命名資料表時,請務必確認該資料表上的外部索引鍵條件(Foreign Key Constraint)是否有直接設定名稱,而不是使用 Laravel 所指定的基於慣例的名稱。若未直接設定名稱,則這些外部索引鍵條件的名稱可能會參照到舊的資料表名稱:

欄位

建立欄位

Schema Facade 上的 table 方法可用來更新現有的資料表。與 create 方法類似,table 方法接受兩個因數:資料表名稱,以及一個接收 Illuminate\Database\Schema\Blueprint 實體的閉包。使用這個 Blueprint 實體,即可用來在資料表上新增欄位或索引:

1use Illuminate\Database\Schema\Blueprint;
2use Illuminate\Support\Facades\Schema;
3 
4Schema::table('users', function (Blueprint $table) {
5 $table->integer('votes');
6});
1use Illuminate\Database\Schema\Blueprint;
2use Illuminate\Support\Facades\Schema;
3 
4Schema::table('users', function (Blueprint $table) {
5 $table->integer('votes');
6});

可用的欄位型別

Schema Builder Blueprint 提供了多種方法,這些方法對應到可新增至資料庫資料表中各種不同的欄位型別。可用的各個方法列在下表中:

bigIncrements()

bigIncrements 方法建立一個 Auto-Increment(自動遞增)UNSIGNED BIGINT (主索引鍵(Primary Key)) 或相等欄位:

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

bigInteger()

bigInteger 方法建立一個 BIGINT 或相等的欄位:

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

binary()

binary 方法建立一個 BLOB 或相等欄位:

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

boolean()

boolean 方法建立一個 BOOLEAN 或相等欄位:

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

char()

char 方法以給定的長度來建立一個 CHAR 或相等欄位:

1$table->char('name', 100);
1$table->char('name', 100);

dateTimeTz()

dateTimeTz 方法以給定的精度 (總位數) 建立一個 DATETIME (含時區) 或相等欄位:

1$table->dateTimeTz('created_at', $precision = 0);
1$table->dateTimeTz('created_at', $precision = 0);

dateTime()

dateTime 方法會使用給定的可選精度 (總位數) 來建立一個 DATETIME 或相等欄位:

1$table->dateTime('created_at', $precision = 0);
1$table->dateTime('created_at', $precision = 0);

date()

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

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

decimal()

decimal 方法會以給定的精度(Precision) (總位數) 與小數位數(Scale) (小數位數) 來建立一個 DECIMAL 或相等欄位:

1$table->decimal('amount', $precision = 8, $scale = 2);
1$table->decimal('amount', $precision = 8, $scale = 2);

double()

double 方法會以給定的精度(Precision) (總位數) 與小數位數(Scale) (小數位數) 來建立一個 DOUBLE 或相等欄位:

1$table->double('amount', 8, 2);
1$table->double('amount', 8, 2);

enum()

enum 方法以給定的有效值來建立一個 ENUM 或相等欄位:

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

float()

float 方法會以給定的精度(Precision) (總位數) 與小數位數(Scale) (小數位數) 來建立一個 FLOAT 或相等欄位:

1$table->float('amount', 8, 2);
1$table->float('amount', 8, 2);

foreignId()

foreignId 方法會建立一個 UNSIGNED BIGINT 或相等的欄位:

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

foreignIdFor()

foreighIdFor 方法會以給定的 Model 類別來建立一個 {欄位}_id UNSIGNED BIGINT 或相等欄位:

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

foreignUuid()

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

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

geometryCollection()

geometryCollection 方法會建立一個 GEOMETRYCOLLECTION 或相等欄位:

1$table->geometryCollection('positions');
1$table->geometryCollection('positions');

geometry()

geometry 方法建立一個 GEOMETRY 或相等欄位:

1$table->geometry('positions');
1$table->geometry('positions');

id()

id 欄位為 bigIncrements 方法的別名。預設情況下,該方法會建立一個 id 欄位。不過,若想為該欄位指定不同的名稱,也可以傳入欄位名稱:

1$table->id();
1$table->id();

increments()

increments 方法會建立一個 Auto-Increment(自動遞增)UNSIGNED INTEGER 或同等欄位作為主索引鍵(Primary Key)

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

integer()

integer 方法建立一個 INTEGER 或相等的欄位:

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

ipAddress()

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

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

json()

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

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

jsonb()

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

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

lineString()

lineString 方法建立一個 LINESTRING 或相等的欄位:

1$table->lineString('positions');
1$table->lineString('positions');

longText()

longText 方法建立一個 LONGTEXT 或相等欄位:

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

macAddress()

macAddress 方法會建立一個用來保存 MAC 位址的欄位。在某些資料庫系統,如 PostgreSQL 中,有專門的欄位可用來保存這種類型的資料。在其他資料庫系統,則會使用相等的字串欄位:

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

mediumIncrements()

mediumIncrements 方法會建立一個 Auto-Increment(自動遞增)UNSIGNED MEDIUMINT 或同等欄位作為主索引鍵(Primary Key)

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

mediumInteger()

mediumInteger 方法建立一個 MEDIUMINT 或相等的欄位:

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

mediumText()

mediumText 方法建立一個 MEDIUMTEXT 或相等的欄位:

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

morphs()

morphs 是一個方便方法,會新增一個 {欄位}_id UNSIGNED BIGINT 或相等欄位,以及一個 {欄位}_type VARCHAR 或想等欄位。

該方法主要是要給多型 Eloquent 關聯定義欄位用的。在下列範例中,會建立 taggable_idtaggable_type 欄位:

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

multiLineString()

multiLineString 方法建立一個 MULTILINESTRING 或相等的欄位:

1$table->multiLineString('positions');
1$table->multiLineString('positions');

multiPoint()

multiPoint 方法建立一個 MULTIPOINT 或相等的欄位:

1$table->multiPoint('positions');
1$table->multiPoint('positions');

multiPolygon()

multiPolygon 方法建立一個 MULTIPOLYGON 或相等的欄位:

1$table->multiPolygon('positions');
1$table->multiPolygon('positions');

nullableTimestamps()

nullabaleTimestamps 方法是 timestamps 方法的別名:

1$table->nullableTimestamps(0);
1$table->nullableTimestamps(0);

nullableMorphs()

該方法與 morphs 方法類似。不過,使用 nullableMorphs 方法建立的欄位會是「nullable」的:

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

nullableUuidMorphs()

該方法與 uuidMorphs 方法類似。不過,使用 nullableMorphs 方法建立的欄位會是「nullable」的:

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

point()

point 方法會建立一個 POINT 或相等欄位:

1$table->point('position');
1$table->point('position');

polygon()

polygon 方法建立一個 POLYGON 或相等的欄位:

1$table->polygon('position');
1$table->polygon('position');

rememberToken()

rememberToken 方法會建立一個 Nullable 的 VARCHAR(100) 或相等的欄位,用於存放目前的「記住我」身份驗證權杖

1$table->rememberToken();
1$table->rememberToken();

set()

set 方法會以給定的有效值來建立一個 SET 或相等欄位:

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

smallIncrements()

smallIncrements 方法會建立一個 Auto-Increment(自動遞增)UNSIGNED SMALLINT 或同等欄位作為主索引鍵(Primary Key)

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

smallInteger()

smallInteger 方法建立一個 SMALLINT 或相等的欄位:

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

softDeletesTz()

softDeletesTz 方法會以給定的可選精度(Precision) (總位數) 新增一個 Nullable 的 deleted_at TIMESTAMP (含時區) 或相等欄位。該欄位主要是給 Eloquent「軟刪除」功能使用的,用來保存 deleted_at 時戳:

1$table->softDeletesTz($column = 'deleted_at', $precision = 0);
1$table->softDeletesTz($column = 'deleted_at', $precision = 0);

softDeletes()

softDeletes 方法會以給定的可選精度(Precision) (總位數) 新增一個 Nullable 的 deleted_at TIMESTAMP 或相等欄位。該欄位主要是給 Eloquent「軟刪除」功能使用的,用來保存 deleted_at 時戳:

1$table->softDeletes($column = 'deleted_at', $precision = 0);
1$table->softDeletes($column = 'deleted_at', $precision = 0);

string()

string 方法以給定的長度來建立一個 VARCHAR 或相等欄位:

1$table->string('name', 100);
1$table->string('name', 100);

text()

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

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

timeTz()

timeTz 方法以給定的精度 (總位數) 建立一個 TIME (含時區) 或相等欄位:

1$table->timeTz('sunrise', $precision = 0);
1$table->timeTz('sunrise', $precision = 0);

time()

time 方法會使用給定的可選精度 (總位數) 來建立一個 TIME 或相等欄位:

1$table->time('sunrise', $precision = 0);
1$table->time('sunrise', $precision = 0);

timestampTz()

timestampTz 方法以給定的精度 (總位數) 建立一個 TIMESTAMP (含時區) 或相等欄位:

1$table->timestampTz('added_at', $precision = 0);
1$table->timestampTz('added_at', $precision = 0);

timestamp()

timestamp 方法會使用給定的可選精度 (總位數) 來建立一個 TIMESTAMP 或相等欄位:

1$table->timestamp('added_at', $precision = 0);
1$table->timestamp('added_at', $precision = 0);

timestampsTz()

timestampsTz 方法以給定可選精度(Precision) (總位數) 建立 TIMESTAMP (含時區) 或相等的 created_atupdated_at 欄位:

1$table->timestampsTz($precision = 0);
1$table->timestampsTz($precision = 0);

timestamps()

timestamps 方法以給定可選精度(Precision) (總位數) 建立 TIMESTAMP 或相等的 created_atupdated_at 欄位:

1$table->timestamps($precision = 0);
1$table->timestamps($precision = 0);

tinyIncrements()

tinyIncrements 方法會建立一個 Auto-Increment(自動遞增)UNSIGNED TINYINT 或同等欄位作為主索引鍵(Primary Key)

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

tinyInteger()

tinyInteger 方法建立一個 TINYINT 或相等的欄位:

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

tinyText()

tinyText 方法建立一個 TINYTEXT 或相等欄位:

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

unsignedBigInteger()

unsignedBigInteger 方法會建立一個 UNSIGNED BIGINT 或相等的欄位:

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

unsignedDecimal()

unsignedDecimal 方法會以給定的可選精度(Precision) (總位數) 與小數位數(Scale) (小數位數) 來建立一個 UNSIGNED DECIMAL 或相等欄位:

1$table->unsignedDecimal('amount', $precision = 8, $scale = 2);
1$table->unsignedDecimal('amount', $precision = 8, $scale = 2);

unsignedInteger()

unsignedInteger 方法會建立一個 UNSIGNED INTEGER 或相等的欄位:

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

unsignedMediumInteger()

unsignedMediumInteger 方法會建立一個 UNSIGNED MEDIUMINT 或相等的欄位:

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

unsignedSmallInteger()

unsignedSmallInteger 方法會建立一個 UNSIGNED SMALLINT 或相等的欄位:

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

unsignedTinyInteger()

unsignedTinyInteger 方法會建立一個 UNSIGNED TINYINT 或相等的欄位:

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

uuidMorphs()

uuidMorphs 是一個方便方法,會新增一個 {欄位}_id CHAR(36) 或相等欄位,以及一個 {欄位}_type VARCHAR 或想等欄位。

該方法主要是要給使用 UUID 作為識別字元的多型 Eloquent 關聯定義欄位用的。在下列範例中,會建立 taggable_idtaggable_type 欄位:

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

uuid()

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

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

year()

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

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

欄位修飾詞

除了上列欄位型別外,還有多種可在將欄位新增至資料表時使用的欄位「修飾詞」。舉例來說,若要將欄位設為「Nullable」,可使用 nullable 方法:

1use Illuminate\Database\Schema\Blueprint;
2use Illuminate\Support\Facades\Schema;
3 
4Schema::table('users', function (Blueprint $table) {
5 $table->string('email')->nullable();
6});
1use Illuminate\Database\Schema\Blueprint;
2use Illuminate\Support\Facades\Schema;
3 
4Schema::table('users', function (Blueprint $table) {
5 $table->string('email')->nullable();
6});

下表中包含了所有可用的修飾詞。該列表中未包含索引修飾詞

修飾詞說明
->after('column')將欄位放在另一個欄位「之後(After)」(MySQL)。
->autoIncrement()將 INTEGER 欄位設為 Auto-Increment(自動遞增) (主索引鍵(Primary Key))。
->charset('utf8mb4')指定用於該欄位的 Character Set (MySQL)。
->collation('utf8mb4_unicode_ci')指定用於該欄位的 Collation (MySQL/PostgreSQL/SQL Server)。
->comment('my comment')為該欄位新增註解 (MySQL/PostgreSQL)。
->default($value)為欄位指定「預設(Default)」值。
->first()將欄位放在資料表中的「第一個(First)」欄位 (MySQL)。
->from($integer)設定 Auto-Increment(自動遞增) 欄位的起始值 (MySQL / PostgreSQL)。
->invisible()讓該欄位在 SELECT * 查詢中「不可見(Invisible)」(MySQL)。
->nullable($value = true)允許將 NULL 值插入該欄位中。
->storedAs($expression)建立一個 Stored Generated 的欄位 (MySQL / PostgreSQL)。
->unsigned()將 INTEGER 欄位設為 UNSIGNED (MySQL)。
->useCurrent()設定 TIMESTAMP 欄位使用 CURRENT_TIMESTAMP 作為預設值。
->useCurrentOnUpdate()在資料更新時,將 TIMESTAMP 欄位設為 CURRENT_TIMESTAMP。
->virtualAs($expression)建立一個 Stored Generated 的欄位 (MySQL / PostgreSQL / SQLite)。
->generatedAs($expression)以指定的 Sequence(順序) 選項來建立 Identity 欄位 (PostgreSQL)。
->always()定義一個優先使用 Sequence 值而不使用輸入值的 Identity 欄位 (PostgreSQL)。
->isGeometry()將 Spatial 欄位的型別設為 geometry —— 即 geography 的預設型別 (PostgreSQL)。

預設運算式

default 修飾詞可接受 Illuminate\Database\Query\Expression 實體。使用 Expression 實體時,Laravel 就不會將輸入值包裝在引號內,能讓我們使用資料庫所提供的函式。有一些狀況特別適合使用這個方法,如要給 JSON 欄位指定預設值時:

1<?php
2 
3use Illuminate\Support\Facades\Schema;
4use Illuminate\Database\Schema\Blueprint;
5use Illuminate\Database\Query\Expression;
6use Illuminate\Database\Migrations\Migration;
7 
8class CreateFlightsTable extends Migration
9{
10 /**
11 * Run the migrations.
12 *
13 * @return void
14 */
15 public function up()
16 {
17 Schema::create('flights', function (Blueprint $table) {
18 $table->id();
19 $table->json('movies')->default(new Expression('(JSON_ARRAY())'));
20 $table->timestamps();
21 });
22 }
23}
1<?php
2 
3use Illuminate\Support\Facades\Schema;
4use Illuminate\Database\Schema\Blueprint;
5use Illuminate\Database\Query\Expression;
6use Illuminate\Database\Migrations\Migration;
7 
8class CreateFlightsTable extends Migration
9{
10 /**
11 * Run the migrations.
12 *
13 * @return void
14 */
15 public function up()
16 {
17 Schema::create('flights', function (Blueprint $table) {
18 $table->id();
19 $table->json('movies')->default(new Expression('(JSON_ARRAY())'));
20 $table->timestamps();
21 });
22 }
23}
exclamation

對於預設運算式的支援程度會因資料庫 Driver、資料庫版本、欄位型別等而有所不同。請參考資料庫的說明文件。

欄位順序

在使用 MySQL 資料庫時,可使用 after 方法來將欄位插入到資料表結構中的某個現有欄位之後:

1$table->after('password', function ($table) {
2 $table->string('address_line1');
3 $table->string('address_line2');
4 $table->string('city');
5});
1$table->after('password', function ($table) {
2 $table->string('address_line1');
3 $table->string('address_line2');
4 $table->string('city');
5});

修改欄位

前置要求

要修改欄位前,必須先使用 Composer 套件管理員來安裝 doctrine/dbal 套件。Doctrine DBAL 函式庫要用來判斷目前欄位的狀態,並用以建立要修改欄位所需要的 SQL 查詢:

1composer require doctrine/dbal
1composer require doctrine/dbal

若有需要修改使用 timestamp 方法建立的欄位,則必須在 config/database.php 設定檔中加上下列設定:

1use Illuminate\Database\DBAL\TimestampType;
2 
3'dbal' => [
4 'types' => [
5 'timestamp' => TimestampType::class,
6 ],
7],
1use Illuminate\Database\DBAL\TimestampType;
2 
3'dbal' => [
4 'types' => [
5 'timestamp' => TimestampType::class,
6 ],
7],
exclamation

使用 Microsoft SQL Server 時,請確保有安裝 doctrine/dbal:^3.0

更新欄位屬性

使用 change 欄位,即可修改現有欄位的型別或屬性。舉例來說,我們可以用來增加 string 欄位的大小。來看看使用 change 方法的例子,我們來把 name 欄位的大小從 25 增加到 50。若要增加 name 欄位的大小,我們只需要定義該欄位的新狀態,然後呼叫 change 方法即可:

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

我們也可以將某個欄位更改為 nullable:

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

可修改的欄位型別有:bigIntegerbinarybooleandatedateTimedateTimeTzdecimalintegerjsonlongTextmediumTextsmallIntegerstringtexttimeunsignedBigIntegerunsignedIntegerunsignedSmallIntegeruuid 等。若要修改 timestamp 欄位型別,則必須先將其註冊為 Doctrine 型別

重新命名欄位

若要重新命名欄位,可使用 Schema Builder Blueprint 提供的 renameColumn 方法。在重新命名欄位前,請先確認是否有使用 Composer 套件管理員安裝 doctrine/dbal 函式庫:

1Schema::table('users', function (Blueprint $table) {
2 $table->renameColumn('from', 'to');
3});
1Schema::table('users', function (Blueprint $table) {
2 $table->renameColumn('from', 'to');
3});
exclamation

目前尚不支援重新命名 enum 欄位。

刪除欄位

若要刪除欄位,可使用 Schema Builder Blueprint 上的 dropColumn 方法。若為使用 SQLite 資料庫的專案,則在使用 dropColumn 前必須先使用 Composer 套件管理員安裝 doctrine/dbal 套件:

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

可以傳入一組欄位名稱的陣列給 dropColumn 方法來一次移除多個欄位:

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

使用 SQLite 資料庫時,不支援在單一 Migration 中刪除或修改多個欄位。

可用的指令別名

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 Builder 支援多種類型的索引。下列為一個建立新 email 欄位並指定該欄位值不可重複(Unique)的範例。若要建立索引,我們可以將 unique 方法串聯到欄位定義之後呼叫:

1use Illuminate\Database\Schema\Blueprint;
2use Illuminate\Support\Facades\Schema;
3 
4Schema::table('users', function (Blueprint $table) {
5 $table->string('email')->unique();
6});
1use Illuminate\Database\Schema\Blueprint;
2use Illuminate\Support\Facades\Schema;
3 
4Schema::table('users', function (Blueprint $table) {
5 $table->string('email')->unique();
6});

或者,我們也可以在定義完欄位後再建立索引。若要在定義欄位完後才建立索引,我們需要在 Schema Builder Blueprint 上呼叫 unique 方法。該方法的引數為要設為 Unique 索引的欄位名稱:

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

我們也可以傳入一組欄位的陣列給索引方法,以建立一個複合式(Compound) / 組合式(Composite)的索引

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

建立索引時,Laravel 會自動依據資料表名稱、欄位名稱、索引類型等來產生索引名稱。不過,我們也可以傳入第二個因數給該方法來自行指定索引名稱:

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

可用的索引類型

Laravel 的 Schema Builder Blueprint 為 Laravel 所支援的各種索引類型都提供了方法。每個索引方法都接受可選的第二引數,可用來指定索引的名稱。若未提供索引名稱,則會自動使用索引的資料表名稱、欄位名稱、以及索引型別等來產生索引名稱。下表為可用的索引方法:

指令說明
$table->primary('id');新增主索引鍵(Primary Key)
$table->primary(['id', 'parent_id']);新增複合式索引鍵(Composite Keys)
$table->unique('email');新增 Unique(不重複) 索引
$table->index('state');新增索引。
$table->fulltext('body');新增 Fulltext(全文) 索引 (MySQL/PostgreSQL)。
$table->fulltext('body')->language('english');以指定的語言來新增 Fulltext(全文) 索引 (PostgreSQL)。
$table->spatialIndex('location');新增 Spatial(空間) 索引 (除了 SQLite)。

索引的長度與 MySQL / MariaDB

預設情況下,Laravel 使用 utf8mb4 Character Set。若使用的 MySQL 版本小於 5.7.7,或是小於 10.2.2 版的 MariaDB,則需要手動調整 Migration 產生的預設字串長度,以讓 MySQL 能為這些字串欄位建立索引。可以在 App\Providers\AppServiceProvider 類別的 boot 方法內呼叫 Schema::defaultStringLength 方法來調整預設的字串長度:

1use Illuminate\Support\Facades\Schema;
2 
3/**
4 * Bootstrap any application services.
5 *
6 * @return void
7 */
8public function boot()
9{
10 Schema::defaultStringLength(191);
11}
1use Illuminate\Support\Facades\Schema;
2 
3/**
4 * Bootstrap any application services.
5 *
6 * @return void
7 */
8public function boot()
9{
10 Schema::defaultStringLength(191);
11}

或者,也可以啟用資料庫的 innodb_large_prefix 選項。請參考所使用資料庫的說明文件,以瞭解如何正確啟用該選項。

重新命名索引

若要重新命名索引,可使用 Schema Builder Blueprint 提供的 renameIndex 方法。該方法的第一個引數為目前的索引名稱,而第二個引數則為要修改的名稱:

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

刪除索引

若要刪除索引,則需要指定索引的名稱。預設情況下,Laravel 會自動依照資料表名稱、索引欄位名稱、以及索引類型來指派索引名稱。範例如下:

指令說明
$table->dropPrimary('users_id_primary');在「users」資料表內刪除主索引鍵(Primary Key)
$table->dropUnique('users_email_unique');從「users」資料表中刪除 Unique 索引。
$table->dropIndex('geo_state_index');從「geo」資料表中刪除一般索引。
$table->dropSpatialIndex('geo_location_spatialindex');從「geo」資料表中刪除 Spatial 索引 (除了 SQLite)。

在刪除索引上時,若傳入一組欄位陣列給該方法,則會自動依照資料表名稱、欄位名稱、索引型別等產生慣例式的索引名稱:

1Schema::table('geo', function (Blueprint $table) {
2 $table->dropIndex(['state']); // 刪除 'geo_state_index' 索引
3});
1Schema::table('geo', function (Blueprint $table) {
2 $table->dropIndex(['state']); // 刪除 'geo_state_index' 索引
3});

Foreign Key Constraint

在 Laravel 中,也可以建立 Foreign Key Constraint(外部索引鍵的條件約束)。使用 Foreigh Key Constraint,就可在資料庫層級上強制確保參照的完整性。舉例來說,我們來在 posts 資料表上定義一個參照到 users 資料表 id 欄位的 user_id 欄位:

1use Illuminate\Database\Schema\Blueprint;
2use Illuminate\Support\Facades\Schema;
3 
4Schema::table('posts', function (Blueprint $table) {
5 $table->unsignedBigInteger('user_id');
6 
7 $table->foreign('user_id')->references('id')->on('users');
8});
1use Illuminate\Database\Schema\Blueprint;
2use Illuminate\Support\Facades\Schema;
3 
4Schema::table('posts', function (Blueprint $table) {
5 $table->unsignedBigInteger('user_id');
6 
7 $table->foreign('user_id')->references('id')->on('users');
8});

由於這個語法有點複雜,因此 Laravel 提供了一個額外的、簡潔的方法。這種方法使用慣例,來提供更好的開發者經驗(DX)。在使用 foreignID 方法建立欄位時,上方的範例可以被這樣改寫:

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

foreignId 方法會建立一個 UNSIGNED BIGINT 或相等欄位,而 constrained 方法會使用慣例來判斷要參照的資料表與欄位。若表名不符合 Laravel 的慣例,可在 constrained 方法的第二個引數上指定資料表名稱:

1Schema::table('posts', function (Blueprint $table) {
2 $table->foreignId('user_id')->constrained('users');
3});
1Schema::table('posts', function (Blueprint $table) {
2 $table->foreignId('user_id')->constrained('users');
3});

也可以指定 Constraint「on delete」與「on update」屬性的動作:

1$table->foreignId('user_id')
2 ->constrained()
3 ->onUpdate('cascade')
4 ->onDelete('cascade');
1$table->foreignId('user_id')
2 ->constrained()
3 ->onUpdate('cascade')
4 ->onDelete('cascade');

或者,也有比較描述性的語法可以設定這些動作:

方法說明
$table->cascadeOnUpdate();更新時應串聯更新 (Cascade)。
$table->restrictOnUpdate();更新時應限制更新 (Restricted)。
$table->cascadeOnDelete();刪除時應串聯刪除 (Cascade)。
$table->restrictOnDelete();刪除時應限制刪除 (Restricted)。
$table->nullOnDelete();刪除時應將外部索引鍵設為 null。

若有額外的欄位修飾詞,應將其放在 constrained 方法前呼叫:

1$table->foreignId('user_id')
2 ->nullable()
3 ->constrained();
1$table->foreignId('user_id')
2 ->nullable()
3 ->constrained();

刪除外部索引鍵

若要刪除 Foreign Key(外部索引鍵),可使用 dropForeign 方法,傳入要刪除的 Foreign Key Constraint(外部索引鍵條件約束) 名稱即可。Foreign Key Constraint 使用與索引相同的命名規範。換句話說,Foreign Key Constraint 的名稱會使用要約束的資料表名稱與欄位名稱組成,並在後方加上「_foreign」後置詞:

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

或者,也可以傳入一組包含欄位名稱的陣列給 dropForeign 方法。這組陣列中應包含 Foreign Key 的名稱。傳入該陣列後,Laravel 會使用約束的命名管理來將該陣列轉換為 Foreign Key Constraint 的名稱:

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

啟用/禁用 Foreign Key Constraint

可以使用下列方法來在 Migration 中啟用或禁用 Foreign Key Constraint:

1Schema::enableForeignKeyConstraints();
2 
3Schema::disableForeignKeyConstraints();
1Schema::enableForeignKeyConstraints();
2 
3Schema::disableForeignKeyConstraints();
exclamation

SQLite 預設會禁用 Foreign Key Constraint。使用 SQLite 時,在 Migration 中建立 Foreign Key Constraint 前,請先檢查是否有在資料庫設定中啟用 Foreign Key 支援。此外,SQLite 只支援在建立資料表時設定 Foreign Key,而無法在修改資料表時新增

Event

為了讓開發更方便,Migration 中的各個動作都會分派 [Event(/docs/8.x/events)](事件)。下列的所有 Event 都繼承了 Illuminate\Database\Events\MigrationEvent 類別:

類別說明
Illuminate\Database\Events\MigrationsStarted即將執行一批 Migration。
Illuminate\Database\Events\MigrationsEnded已完成執行一批 Migration。
Illuminate\Database\Events\MigrationStarted即將執行單一 Migration。
Illuminate\Database\Events\MigrationEnded已完成執行單一 Migration。
翻譯進度
100% 已翻譯
更新時間:
2024年6月30日 上午8:27:00 [世界標準時間]
翻譯人員:
  • cornch
幫我們翻譯此頁

留言

尚無留言

“Laravel” is a Trademark of Taylor Otwell.
The source documentation is released under MIT license. See laravel/docs on GitHub for details.
The translated documentations are released under MIT license. See cornch/laravel-docs-l10n on GitHub for details.