資料庫: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
選項。給定的路徑應為相對於專案根目錄的相對路徑。
可以安裝 Stub 來自訂 Migration 的 Stub。
壓縮 Migration
在我們持續撰寫專案的同時,我們可能會逐漸累積出越來越多的資料庫 Migration 檔。這樣可能會導致 database/migrations
目錄中包含了數百個 Migration 檔。若有需要的話,我們可以將 Migration 檔「壓縮」進單一 SQL 檔內。要開始壓縮,請執行 schema:dump
指令:
1php artisan schema:dump23# 傾印目前的資料庫結構,並刪除所有現存的 Migration...4php artisan schema:dump --prune
1php artisan schema:dump23# 傾印目前的資料庫結構,並刪除所有現存的 Migration...4php artisan schema:dump --prune
執行該指令時,Laravel 會將一個「Schema」檔案寫入 database/schema
目錄內。Schema 檔案的名稱對影到資料庫連線的名稱。當要移轉資料庫時,若尚未執行過任何 Migration,Laravel 會先執行目前正在使用的資料庫連線所對應 Schema 檔中的 SQL。執行完 Schema 檔內的陳述式後,Laravel 才會接著執行不在該 Schema 傾印中剩下的 Migration。
若專案的測試使用的資料庫連線與本機開發環境所使用的連線不同時,請確認是否有使用該資料庫連線傾印 Schema 檔案,這樣測試中才能正常的建立資料庫。通常這個步驟應放在將開發環境所使用的資料庫連線傾印出來之後:
1php artisan schema:dump2php artisan schema:dump --database=testing --prune
1php artisan schema:dump2php artisan schema:dump --database=testing --prune
請將資料庫 Schema 檔 Commit 進版本控制中,好讓團隊中其他的新開發人員可快速建立專案的初始資料庫結構。
Migration 壓縮只支援 MySQL、PostgreSQL、SQLite 等資料庫,且會使用資料庫的主控台用戶端。Schema 傾印無法用來復原 In-Memory 的 SQLite 資料庫。
Migration 的架構
Migration 類別中包含了兩個方法:up
與 down
。up
方法可用來在資料庫中新增新資料表、欄位、索引等;而 down
方法則用來做與 up
方法相反的事。
在這兩個方法中,我們可以使用 Laravel 的 Schema Builder 來以描述性的方法建立與修改資料表。若要瞭解 Schema
Builder 中所有可用的方法,請參考 Schema Builder 的說明文件。舉例來說,下列 Migration 會建立一個 flights
資料表:
1<?php23use Illuminate\Database\Migrations\Migration;4use Illuminate\Database\Schema\Blueprint;5use Illuminate\Support\Facades\Schema;67return new class extends Migration8{9 /**10 * Run the migrations.11 */12 public function up(): void13 {14 Schema::create('flights', function (Blueprint $table) {15 $table->id();16 $table->string('name');17 $table->string('airline');18 $table->timestamps();19 });20 }2122 /**23 * Reverse the migrations.24 */25 public function down(): void26 {27 Schema::drop('flights');28 }29};
1<?php23use Illuminate\Database\Migrations\Migration;4use Illuminate\Database\Schema\Blueprint;5use Illuminate\Support\Facades\Schema;67return new class extends Migration8{9 /**10 * Run the migrations.11 */12 public function up(): void13 {14 Schema::create('flights', function (Blueprint $table) {15 $table->id();16 $table->string('name');17 $table->string('airline');18 $table->timestamps();19 });20 }2122 /**23 * Reverse the migrations.24 */25 public function down(): void26 {27 Schema::drop('flights');28 }29};
設定 Migration 的連線
若 Migration 會使用與專案預設資料庫連線不同的資料庫連線,則請在 Migration 中設定 $connection
屬性:
1/**2 * The database connection that should be used by the migration.3 *4 * @var string5 */6protected $connection = 'pgsql';78/**9 * Run the migrations.10 */11public function up(): void12{13 // ...14}
1/**2 * The database connection that should be used by the migration.3 *4 * @var string5 */6protected $connection = 'pgsql';78/**9 * Run the migrations.10 */11public function up(): void12{13 // ...14}
執行 Migration
若要執行所有尚未執行過的 Migration,請執行 migrate
Artisan 指令:
1php artisan migrate
1php artisan migrate
若想檢視目前為止已執行了哪些 Migration,可使用 migrate:status
Artisan 指令:
1php artisan migrate:status
1php artisan migrate:status
若想檢視 Migration 中會執行的 SQL 陳述式而不實際執行這些 SQL,則可提供 --pretend
旗標給 migrate
指令:
1php artisan migrate --pretend
1php artisan migrate --pretend
獨立執行 Migration
若專案會被部署到多個伺服器上,而 Migration 是部署流程的一部分的話,我們通常不會希望有兩台 Server 同時嘗試執行資料庫移轉。若要避免有兩台 Server 同時執行,可以在呼叫 migrate
指令時加上 isolated
選項。
提供了 isolated
選項後,Laravel 會嘗試使用專案的快取 Driver 來取得一個 Atomic Lock,然後再執行 Migration。當 Lock 被鎖住時,嘗試執行 migrate
指令就不會執行。不過,這些不被執行的 migrate
指令仍然會回傳成功的終止狀態代碼:
1php artisan migrate --isolated
1php artisan migrate --isolated
若要使用此功能,則應用程式必須要使用 memcached
, redis
, dynamodb
, database
, file
或 array
作為應用程式的預設快取 Driver。另外,所有的伺服器也都必須要連線至相同的中央快取伺服器。
在正式環境中強制執行 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
只要提供 batch
選項給 rollback
指令,就可以復原特定「批次」的 Migration。這裡的 batch
選項,對應到專案資料庫中 migrations
資料表的 batch 值。舉例來說,下列指令會復原所有第 3 批次的 Migration:
1php artisan migrate:rollback --batch=3
1php artisan migrate:rollback --batch=3
若想檢視 Migration 中會執行的 SQL 陳述式而不實際執行這些 SQL,則可提供 --pretend
旗標給 migrate:rollback
指令:
1php artisan migrate:rollback --pretend
1php artisan migrate:rollback --pretend
migrate:reset
指令會復原專案中所有的 Migration:
1php artisan migrate:reset
1php artisan migrate:reset
以單一指令來復原並 Migrate
migrate:refresh
指令會將所有的 Migration 都復原回去,並接著執行 migrate
指令。使用該指令,就可以有效率的重建整個資料庫:
1php artisan migrate:refresh23# 重新整理資料庫,並執行所有的資料庫 Seed...4php artisan migrate:refresh --seed
1php artisan migrate:refresh23# 重新整理資料庫,並執行所有的資料庫 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:fresh23php artisan migrate:fresh --seed
1php artisan migrate:fresh23php artisan migrate:fresh --seed
不論資料表是否有前置詞 (Prefix),migrate:fresh
指令會刪除所有的資料庫資料表。在使用與其他專案共享的資料庫時,若要與本指令搭配使用請務必注意。
資料表
建立資料表
若要建立新的資料庫資料表,請使用 Schema
Facade 上的 create
方法。create
方法接受兩個引數:資料表名稱、以及一個接收 Blueprint
物件的閉包。Blueprint
物件可用來定義新資料表:
1use Illuminate\Database\Schema\Blueprint;2use Illuminate\Support\Facades\Schema;34Schema::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;34Schema::create('users', function (Blueprint $table) {5 $table->id();6 $table->string('name');7 $table->string('email');8 $table->timestamps();9});
建立資料表時,我們可以使用任意 Schema Builder 的欄位方法來定義資料表欄位。
檢查資料表與欄位是否存在
我們可以使用 hasTable
與 hasColumn
方法來檢查資料表或欄位是否存在:
1if (Schema::hasTable('users')) {2 // 「users」資料表存在...3}45if (Schema::hasColumn('users', 'email')) {6 // 「users」資料表存在,且包含一個「email」欄位...7}
1if (Schema::hasTable('users')) {2 // 「users」資料表存在...3}45if (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';34 // ...5});
1Schema::create('users', function (Blueprint $table) {2 $table->engine = 'InnoDB';34 // ...5});
使用 MySQL 時,charset
與 collation
屬性可用來指定建立資料表的 Character Set 與 Collection:
1Schema::create('users', function (Blueprint $table) {2 $table->charset = 'utf8mb4';3 $table->collation = 'utf8mb4_unicode_ci';45 // ...6});
1Schema::create('users', function (Blueprint $table) {2 $table->charset = 'utf8mb4';3 $table->collation = 'utf8mb4_unicode_ci';45 // ...6});
temporary
方法可用來表示該資料表是「臨時」資料表。臨時資料表只可在目前連線的資料庫工作階段中使用,且會在連線關閉後自動刪除:
1Schema::create('calculations', function (Blueprint $table) {2 $table->temporary();34 // ...5});
1Schema::create('calculations', function (Blueprint $table) {2 $table->temporary();34 // ...5});
若想在資料表上加入「註解」,可在 Table 實體上叫用 comment
方法。資料表註解目前只支援 MySQL 與 Postgres:
1Schema::create('calculations', function (Blueprint $table) {2 $table->comment('Business calculations');34 // ...5});
1Schema::create('calculations', function (Blueprint $table) {2 $table->comment('Business calculations');34 // ...5});
更新資料表
Schema
Facade 上的 table
方法可用來更新現有的資料表。與 create
方法類似,table
方法接受兩個因數:資料表名稱,以及一個接收 Blueprint
實體的閉包。使用 Blueprint
實體,即可用來在資料表上新增欄位或索引:
1use Illuminate\Database\Schema\Blueprint;2use Illuminate\Support\Facades\Schema;34Schema::table('users', function (Blueprint $table) {5 $table->integer('votes');6});
1use Illuminate\Database\Schema\Blueprint;2use Illuminate\Support\Facades\Schema;34Schema::table('users', function (Blueprint $table) {5 $table->integer('votes');6});
重新命名或刪除資料表
若要重新命名現有的資料表,可使用 rename
方法:
1use Illuminate\Support\Facades\Schema;23Schema::rename($from, $to);
1use Illuminate\Support\Facades\Schema;23Schema::rename($from, $to);
若要移除現有的資料表,可使用 drop
或 dropIfExists
方法:
1Schema::drop('users');23Schema::dropIfExists('users');
1Schema::drop('users');23Schema::dropIfExists('users');
與外部索引鍵一起重新命名資料表
在重新命名資料表時,請務必確認該資料表上的外部索引鍵條件是否有直接設定名稱,而不是使用 Laravel 所指定的基於慣例的名稱。若未直接設定名稱,則這些外部索引鍵條件的名稱可能會參照到舊的資料表名稱:
欄位
建立欄位
Schema
Facade 上的 table
方法可用來更新現有的資料表。與 create
方法類似,table
方法接受兩個因數:資料表名稱,以及一個接收 Illuminate\Database\Schema\Blueprint
實體的閉包。使用這個 Blueprint
實體,即可用來在資料表上新增欄位或索引:
1use Illuminate\Database\Schema\Blueprint;2use Illuminate\Support\Facades\Schema;34Schema::table('users', function (Blueprint $table) {5 $table->integer('votes');6});
1use Illuminate\Database\Schema\Blueprint;2use Illuminate\Support\Facades\Schema;34Schema::table('users', function (Blueprint $table) {5 $table->integer('votes');6});
可用的欄位型別
Schema Builder Blueprint 提供了多種方法,這些方法對應到可新增至資料庫資料表中各種不同的欄位型別。可用的各個方法列在下表中:
bigIncrements bigInteger binary boolean char dateTimeTz dateTime date decimal double enum float foreignId foreignIdFor foreignUlid foreignUuid geometryCollection geometry id increments integer ipAddress json jsonb lineString longText macAddress mediumIncrements mediumInteger mediumText morphs multiLineString multiPoint multiPolygon nullableMorphs nullableTimestamps nullableUlidMorphs nullableUuidMorphs point polygon rememberToken set smallIncrements smallInteger softDeletesTz softDeletes string text timeTz time timestampTz timestamp timestampsTz timestamps tinyIncrements tinyInteger tinyText unsignedBigInteger unsignedDecimal unsignedInteger unsignedMediumInteger unsignedSmallInteger unsignedTinyInteger ulidMorphs uuidMorphs ulid uuid year
bigIncrements()
bigIncrements
方法建立一個 Auto-Increment 的 UNSIGNED BIGINT
(主索引鍵) 或相等欄位:
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
方法會以給定的精度 (總位數) 與小數位數 (小數位數) 來建立一個 DECIMAL
或相等欄位:
1$table->decimal('amount', $precision = 8, $scale = 2);
1$table->decimal('amount', $precision = 8, $scale = 2);
double()
double
方法會以給定的精度 (總位數) 與小數位數 (小數位數) 來建立一個 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
方法會以給定的精度 (總位數) 與小數位數 (小數位數) 來建立一個 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()
foreignIdFor
方法會新增一個用於給定 Model 類別的 {column}_id
同等欄位。依照該 Model 的索引鍵型別,該欄位可能為 UNSIGNED BIGINT
、CHAR(36)
或 CHAR(26)
。
1$table->foreignIdFor(User::class);
1$table->foreignIdFor(User::class);
foreignUlid()
foreignUlid
方法會建立一個 ULID
或相等欄位:
1$table->foreignUlid('user_id');
1$table->foreignUlid('user_id');
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
或同等欄位作為主索引鍵:
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');
使用 Postgres 時,會建立 INET
欄位。
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
或同等欄位作為主索引鍵:
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
方法會新增一個用於給定 Model 類別的 {column}_id
同等欄位與 {column}_type
VARCHAR
同等欄位。依照該 Model 的索引鍵型別,{column}_id
欄位可能為 UNSIGNED BIGINT
、CHAR(36)
或 CHAR(26)
型別。
該方法主要是要給多型 Eloquent 關聯定義欄位用的。在下列範例中,會建立 taggable_id
與 taggable_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');
nullableUlidMorphs()
該方法與 ulidMorphs 方法類似。不過,使用 ulidMorphs
方法建立的欄位會是「nullable」的:
1$table->nullableUlidMorphs('taggable');
1$table->nullableUlidMorphs('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
或同等欄位作為主索引鍵:
1$table->smallIncrements('id');
1$table->smallIncrements('id');
smallInteger()
smallInteger
方法建立一個 SMALLINT
或相等的欄位:
1$table->smallInteger('votes');
1$table->smallInteger('votes');
softDeletesTz()
softDeletesTz
方法會以給定的可選精度 (總位數) 新增一個 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
方法會以給定的可選精度 (總位數) 新增一個 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
方法以給定可選精度 (總位數) 建立 TIMESTAMP
(含時區) 或相等的 created_at
與 updated_at
欄位:
1$table->timestampsTz($precision = 0);
1$table->timestampsTz($precision = 0);
timestamps()
timestamps
方法以給定可選精度 (總位數) 建立 TIMESTAMP
或相等的 created_at
與 updated_at
欄位:
1$table->timestamps($precision = 0);
1$table->timestamps($precision = 0);
tinyIncrements()
tinyIncrements
方法會建立一個 Auto-Increment 的 UNSIGNED TINYINT
或同等欄位作為主索引鍵:
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
方法會以給定的可選精度 (總位數) 與小數位數 (小數位數) 來建立一個 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');
ulidMorphs()
ulidMorphs
是一個方便方法,會新增一個 {欄位}_id
CHAR(26)
或相等欄位,以及一個 {欄位}_type
VARCHAR
或相等欄位。
該方法主要是要給使用 ULID 作為識別字元的多型 Eloquent 關聯定義欄位用的。在下列範例中,會建立 taggable_id
與 taggable_type
欄位:
1$table->ulidMorphs('taggable');
1$table->ulidMorphs('taggable');
uuidMorphs()
uuidMorphs
是一個方便方法,會新增一個 {欄位}_id
CHAR(36)
或相等欄位,以及一個 {欄位}_type
VARCHAR
或想等欄位。
該方法主要是要給使用 UUID 作為識別字元的多型 Eloquent 關聯定義欄位用的。在下列範例中,會建立 taggable_id
與 taggable_type
欄位:
1$table->uuidMorphs('taggable');
1$table->uuidMorphs('taggable');
ulid()
ulid
方法會建立一個 ULID
或相等欄位:
1$table->ulid('id');
1$table->ulid('id');
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;34Schema::table('users', function (Blueprint $table) {5 $table->string('email')->nullable();6});
1use Illuminate\Database\Schema\Blueprint;2use Illuminate\Support\Facades\Schema;34Schema::table('users', function (Blueprint $table) {5 $table->string('email')->nullable();6});
下表中包含了所有可用的修飾詞。該列表中未包含索引修飾詞:
修飾詞 | 說明 |
---|---|
->after('column') | 將欄位放在另一個欄位「之後」(MySQL)。 |
->autoIncrement() | 將 INTEGER 欄位設為 Auto-Increment (主索引鍵)。 |
->charset('utf8mb4') | 指定用於該欄位的 Character Set (MySQL)。 |
->collation('utf8mb4_unicode_ci') | 指定用於該欄位的 Collation (MySQL/PostgreSQL/SQL Server)。 |
->comment('my comment') | 為該欄位新增註解 (MySQL/PostgreSQL)。 |
->default($value) | 為欄位指定「預設」值。 |
->first() | 將欄位放在資料表中的「第一個」欄位 (MySQL)。 |
->from($integer) | 設定 Auto-Increment 欄位的起始值 (MySQL / PostgreSQL)。 |
->invisible() | 讓該欄位在 SELECT * 查詢中「不可見」(MySQL)。 |
->nullable($value = true) | 允許將 NULL 值插入該欄位中。 |
->storedAs($expression) | 建立一個 Stored Generated 的欄位 (MySQL / PostgreSQL)。 |
->unsigned() | 將 INTEGER 欄位設為 UNSIGNED (MySQL)。 |
->useCurrent() | 設定 TIMESTAMP 欄位使用 CURRENT_TIMESTAMP 作為預設值。 |
->useCurrentOnUpdate() | 在資料更新時,將 TIMESTAMP 欄位設為 CURRENT_TIMESTAMP (MySQL)。 |
->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<?php23use Illuminate\Support\Facades\Schema;4use Illuminate\Database\Schema\Blueprint;5use Illuminate\Database\Query\Expression;6use Illuminate\Database\Migrations\Migration;78return new class extends Migration9{10 /**11 * Run the migrations.12 */13 public function up(): void14 {15 Schema::create('flights', function (Blueprint $table) {16 $table->id();17 $table->json('movies')->default(new Expression('(JSON_ARRAY())'));18 $table->timestamps();19 });20 }21};
1<?php23use Illuminate\Support\Facades\Schema;4use Illuminate\Database\Schema\Blueprint;5use Illuminate\Database\Query\Expression;6use Illuminate\Database\Migrations\Migration;78return new class extends Migration9{10 /**11 * Run the migrations.12 */13 public function up(): void14 {15 Schema::create('flights', function (Blueprint $table) {16 $table->id();17 $table->json('movies')->default(new Expression('(JSON_ARRAY())'));18 $table->timestamps();19 });20 }21};
對於預設運算式的支援程度會因資料庫 Driver、資料庫版本、欄位型別等而有所不同。請參考資料庫的說明文件。
欄位順序
在使用 MySQL 資料庫時,可使用 after
方法來將欄位插入到資料表結構中的某個現有欄位之後:
1$table->after('password', function (Blueprint $table) {2 $table->string('address_line1');3 $table->string('address_line2');4 $table->string('city');5});
1$table->after('password', function (Blueprint $table) {2 $table->string('address_line1');3 $table->string('address_line2');4 $table->string('city');5});
修改欄位
使用 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});
修改欄位時,必須明顯包含所有要保留在該欄位上的修飾子 (Modifier)。若有未提供的修飾子,則這些屬性會被移除。舉例來說,若要保留 unsigned
、default
、與 comment
屬性,則在修改欄位時必須明顯呼叫這幾個修飾子:
1Schema::table('users', function (Blueprint $table) {2 $table->integer('votes')->unsigned()->default(1)->comment('my comment')->change();3});
1Schema::table('users', function (Blueprint $table) {2 $table->integer('votes')->unsigned()->default(1)->comment('my comment')->change();3});
在 SQLite 上修改欄位
若專案使用 SQLite 資料庫,則必須使用 Composer 套件管理員來安裝 doctrine/dbal
套件,才可以修改欄位。Doctrine DBAL 函式庫要用來判斷目前欄位的狀態,並用以建立要修改欄位所需要的 SQL 查詢:
1composer require doctrine/dbal
1composer require doctrine/dbal
若有需要修改使用 timestamp
方法建立的欄位,則必須在 config/database.php
設定檔中加上下列設定:
1use Illuminate\Database\DBAL\TimestampType;23'dbal' => [4 'types' => [5 'timestamp' => TimestampType::class,6 ],7],
1use Illuminate\Database\DBAL\TimestampType;23'dbal' => [4 'types' => [5 'timestamp' => TimestampType::class,6 ],7],
使用 doctrine/dbal
套件時,可修改的欄位型別有:bigInteger,
binary,
boolean,
char,
date,
dateTime,
dateTimeTz,
decimal,
double,
integer,
json,
longText,
mediumText,
smallInteger,
string,
text,
time,
tinyText,
unsignedBigInteger,
unsignedInteger,
unsignedSmallInteger, 與
uuid`。
重新命名欄位
若要重新命名欄位,可使用 Schema Builder 所提供的 renameColumn
方法:
1Schema::table('users', function (Blueprint $table) {2 $table->renameColumn('from', 'to');3});
1Schema::table('users', function (Blueprint $table) {2 $table->renameColumn('from', 'to');3});
在舊版資料庫中重新命名欄位
若你使用的資料庫版本比下列版本還要老舊,則請確定有在重新命名欄位前使用 Composer 套件管理員安裝 doctrine/dbal
函式庫:
- MySQL <
8.0.3
- MariaDB <
10.5.2
- SQLite <
3.25.0
刪除欄位
若要移除欄位,可使用 Schema Builder 所提供的 dropColumn
方法:
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});
在舊版資料庫中移除欄位
若使用 3.35.0
版以前的 SQLite,則必須先使用 Composer 套件管理員安裝 doctrine/dbal
套件,才能使用 dropColumn
。使用這個套件時,不支援在單一 Migration 內移除或修改多個欄位。
可用的指令別名
Laravel 提供了多種可用來刪除常見欄位型別的方便方法。下表中說明了這些方法:
指令 | 說明 |
---|---|
$table->dropMorphs('morphable'); | 刪除 morphable_id 與 morphable_type 欄位。 |
$table->dropRememberToken(); | 刪除 remember_token 欄位。 |
$table->dropSoftDeletes(); | 刪除 deleted_at 欄位。 |
$table->dropSoftDeletesTz(); | dropSoftDeletes() 方法的別名。 |
$table->dropTimestamps(); | 刪除 created_at 與 updated_at 欄位。 |
$table->dropTimestampsTz(); | dropTimestamps() 方法的別名。 |
索引
建立索引
Laravel 的 Schema Builder 支援多種類型的索引。下列為一個建立新 email
欄位並指定該欄位值不可重複的範例。若要建立索引,我們可以將 unique
方法串聯到欄位定義之後呼叫:
1use Illuminate\Database\Schema\Blueprint;2use Illuminate\Support\Facades\Schema;34Schema::table('users', function (Blueprint $table) {5 $table->string('email')->unique();6});
1use Illuminate\Database\Schema\Blueprint;2use Illuminate\Support\Facades\Schema;34Schema::table('users', function (Blueprint $table) {5 $table->string('email')->unique();6});
或者,我們也可以在定義完欄位後再建立索引。若要在定義欄位完後才建立索引,我們需要在 Schema Builder Blueprint 上呼叫 unique
方法。該方法的引數為要設為 Unique 索引的欄位名稱:
1$table->unique('email');
1$table->unique('email');
我們也可以傳入一組欄位的陣列給索引方法,以建立一個複合式 / 組合式的索引
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'); | 新增主索引鍵。 |
$table->primary(['id', 'parent_id']); | 新增複合式索引鍵。 |
$table->unique('email'); | 新增 Unique 索引 |
$table->index('state'); | 新增索引。 |
$table->fullText('body'); | 新增全文索引 (MySQL/PostgreSQL)。 |
$table->fullText('body')->language('english'); | 以指定的語言來新增全文索引 (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;23/**4 * Bootstrap any application services.5 */6public function boot(): void7{8 Schema::defaultStringLength(191);9}
1use Illuminate\Support\Facades\Schema;23/**4 * Bootstrap any application services.5 */6public function boot(): void7{8 Schema::defaultStringLength(191);9}
或者,也可以啟用資料庫的 innodb_large_prefix
選項。請參考所使用資料庫的說明文件,以瞭解如何正確啟用該選項。
重新命名索引
若要重新命名索引,可使用 Schema Builder Blueprint 提供的 renameIndex
方法。該方法的第一個引數為目前的索引名稱,而第二個引數則為要修改的名稱:
1$table->renameIndex('from', 'to')
1$table->renameIndex('from', 'to')
若專案使用 SQLite 資料庫,則在使用 renameIndex
前必須先使用 Composer 套件管理員安裝 doctrine/dbal
套件:
刪除索引
若要刪除索引,則需要指定索引的名稱。預設情況下,Laravel 會自動依照資料表名稱、索引欄位名稱、以及索引類型來指派索引名稱。範例如下:
指令 | 說明 |
---|---|
$table->dropPrimary('users_id_primary'); | 在「users」資料表內刪除主索引鍵。 |
$table->dropUnique('users_email_unique'); | 從「users」資料表中刪除 Unique 索引。 |
$table->dropIndex('geo_state_index'); | 從「geo」資料表中刪除一般索引。 |
$table->dropFullText('posts_body_fulltext'); | 從「users」資料表中刪除全文索引。 |
$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;34Schema::table('posts', function (Blueprint $table) {5 $table->unsignedBigInteger('user_id');67 $table->foreign('user_id')->references('id')->on('users');8});
1use Illuminate\Database\Schema\Blueprint;2use Illuminate\Support\Facades\Schema;34Schema::table('posts', function (Blueprint $table) {5 $table->unsignedBigInteger('user_id');67 $table->foreign('user_id')->references('id')->on('users');8});
由於這個語法有點複雜,因此 Laravel 提供了一個額外的、簡潔的方法。這種方法使用慣例,來提供更好的開發者經驗。在使用 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(3 table: 'users', indexName: 'posts_user_id'4 );5});
1Schema::table('posts', function (Blueprint $table) {2 $table->foreignId('user_id')->constrained(3 table: 'users', indexName: 'posts_user_id'4 );5});
也可以指定 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();23Schema::disableForeignKeyConstraints();45Schema::withoutForeignKeyConstraints(function () {6 // 此閉包內會禁用外部索引鍵條件限制 (Foreign Key Constraint)...7});
1Schema::enableForeignKeyConstraints();23Schema::disableForeignKeyConstraints();45Schema::withoutForeignKeyConstraints(function () {6 // 此閉包內會禁用外部索引鍵條件限制 (Foreign Key Constraint)...7});
SQLite 預設會禁用 Foreign Key Constraint。使用 SQLite 時,在 Migration 中建立 Foreign Key Constraint 前,請先檢查是否有在資料庫設定中啟用 Foreign Key 支援。此外,SQLite 只支援在建立資料表時設定 Foreign Key,而無法在修改資料表時新增。
Event
為了讓開發更方便,Migration 中的各個動作都會分派 [Event](事件)。下列的所有 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。 |
Illuminate\Database\Events\SchemaDumped | 已傾印完成資料庫結構。 |
Illuminate\Database\Events\SchemaLoaded | 已載入現有的資料庫結構傾印。 |