資料庫: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_table1php 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// Dump the current database schema and prune all existing migrations...4php artisan schema:dump --prune1php artisan schema:dump23// Dump the current database schema and prune all existing migrations...4php artisan schema:dump --prune
執行該指令時,Laravel 會將一個「Schema」檔案寫入 database/schema 目錄內。接著,當要移轉資料庫且尚未執行過任何 Migration 時,Laravel 會先執行該 Schema 檔中的 SQL。執行完 Schema 檔內的陳述式後,接著 Laravel 才會執行不在該 Schema 傾印中剩下的 Migration。
請將資料庫 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;67class CreateFlightsTable extends Migration8{9 /**10 * Run the migrations.11 *12 * @return void13 */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 }2324 /**25 * Reverse the migrations.26 *27 * @return void28 */29 public function down()30 {31 Schema::drop('flights');32 }33}1<?php23use Illuminate\Database\Migrations\Migration;4use Illuminate\Database\Schema\Blueprint;5use Illuminate\Support\Facades\Schema;67class CreateFlightsTable extends Migration8{9 /**10 * Run the migrations.11 *12 * @return void13 */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 }2324 /**25 * Reverse the migrations.26 *27 * @return void28 */29 public function down()30 {31 Schema::drop('flights');32 }33}
匿名 Migration
在上述的範例中可以看到,Laravel 會自動為所有使用 make:migration 指令所產生的所有 Migration 指定一個類別名稱。不過,若有需要的話,我們可以在 Migration 檔中回傳一個匿名類別。若專案中已經有許多的 Migration,且其中某兩個 Migration 發生的類別名稱衝突時,就很適合使用這個方法:
1<?php23use Illuminate\Database\Migrations\Migration;45return new class extends Migration6{7 //8};1<?php23use Illuminate\Database\Migrations\Migration;45return new class extends Migration6{7 //8};
設定 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 *11 * @return void12 */13public function up()14{15 //16}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 *11 * @return void12 */13public function up()14{15 //16}
執行 Migration
若要執行所有尚未執行過的 Migration,請執行 migrate Artisan 指令:
1php artisan migrate1php artisan migrate
若想檢視目前為止已執行了哪些 Migration,可使用 migrate:status Artisan 指令:
1php artisan migrate:status1php artisan migrate:status
在正式環境中強制執行 Migration
有些 Migration 中的動作是破壞性的,也就是一些會導致資料消失的動作。為了避免在正式環境資料庫中執行這些破壞性的動作,因此在執行指令時,會出現提示要求確認。若要強制該指令不跳出提示直接執行,請使用 --force 旗標:
1php artisan migrate --force1php artisan migrate --force
復原 Migration
若要復原最後執行的 Migration 動作,可使用 rollback Artisan 指令。該指令會復原最後「一批」執行的 Migration,其中可能包含多個 Migration 檔:
1php artisan migrate:rollback1php artisan migrate:rollback
我們也可以提供各一個 step 選項給 rollback 指令,以限制要復原的 Migration 數量。舉例來說,下列指令只會復原最後 5 個 Migration:
1php artisan migrate:rollback --step=51php artisan migrate:rollback --step=5
migrate:reset 指令會復原專案中所有的 Migration:
1php artisan migrate:reset1php artisan migrate:reset
以單一指令來復原並 Migrate
migrate:refresh 指令會將所有的 Migration 都復原回去,並接著執行 migrate 指令。使用該指令,就可以有效率的重建整個資料庫:
1php artisan migrate:refresh23// Refresh the database and run all database seeds...4php artisan migrate:refresh --seed1php artisan migrate:refresh23// Refresh the database and run all database seeds...4php artisan migrate:refresh --seed
我們也可以提供各一個 step 選項給 refresh 指令,以限制要復原並重新 Migrate 的 Migration 數量。舉例來說,下列指令只會復原並重新 Migrate 最後 5 個 Migration:
1php artisan migrate:refresh --step=51php artisan migrate:refresh --step=5
刪除所有資料表並 Migrate
migrate:fresh 指令會刪除資料庫中所有資料表,並接著執行 migrate 指令:
1php artisan migrate:fresh23php artisan migrate:fresh --seed1php 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 // The "users" table exists...3}45if (Schema::hasColumn('users', 'email')) {6 // The "users" table exists and has an "email" column...7}1if (Schema::hasTable('users')) {2 // The "users" table exists...3}45if (Schema::hasColumn('users', 'email')) {6 // The "users" table exists and has an "email" column...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});
更新資料表
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 foreignUuid geometryCollection geometry id increments integer ipAddress json jsonb lineString longText macAddress mediumIncrements mediumInteger mediumText morphs multiLineString multiPoint multiPolygon nullableMorphs nullableTimestamps 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 uuidMorphs 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()
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 或同等欄位作為主索引鍵:
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 或同等欄位作為主索引鍵:
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_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');
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');
uuidMorphs()
uuidMorphs 是一個方便方法,會新增一個 {欄位}_id CHAR(36) 或相等欄位,以及一個 {欄位}_type VARCHAR 或想等欄位。
該方法主要是要給使用 UUID 作為識別字元的多型 Eloquent 關聯定義欄位用的。在下列範例中,會建立 taggable_id 與 taggable_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;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。 |
->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;78class CreateFlightsTable extends Migration9{10 /**11 * Run the migrations.12 *13 * @return void14 */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<?php23use Illuminate\Support\Facades\Schema;4use Illuminate\Database\Schema\Blueprint;5use Illuminate\Database\Query\Expression;6use Illuminate\Database\Migrations\Migration;78class CreateFlightsTable extends Migration9{10 /**11 * Run the migrations.12 *13 * @return void14 */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}
對於預設運算式的支援程度會因資料庫 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/dbal1composer 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],
使用 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});
The following column types can be modified: bigInteger, binary, boolean, date, dateTime, dateTimeTz, decimal, integer, json, longText, mediumText, smallInteger, string, text, time, unsignedBigInteger, unsignedInteger, unsignedSmallInteger, and uuid. To modify a timestamp column type a Doctrine type must be registered.
重新命名欄位
若要重新命名欄位,可使用 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});
Renaming an enum column is not currently supported.
刪除欄位
若要刪除欄位,可使用 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});
Dropping or modifying multiple columns within a single migration while using an SQLite database is not supported.
可用的指令別名
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'); |
新增 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;23/**4 * Bootstrap any application services.5 *6 * @return void7 */8public function boot()9{10 Schema::defaultStringLength(191);11}1use Illuminate\Support\Facades\Schema;23/**4 * Bootstrap any application services.5 *6 * @return void7 */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」資料表內刪除主索引鍵。 |
$table->dropUnique('users_email_unique'); |
從「users」資料表中刪除 Unique 索引。 |
$table->dropIndex('geo_state_index'); |
從「geo」資料表中刪除一般索引。 |
$table->dropSpatialIndex('geo_location_spatialindex'); |
Drop a spatial index from the "geo" table (except SQLite). |
在刪除索引上時,若傳入一組欄位陣列給該方法,則會自動依照資料表名稱、欄位名稱、索引型別等產生慣例式的索引名稱:
1Schema::table('geo', function (Blueprint $table) {2 $table->dropIndex(['state']); // Drops index 'geo_state_index'3});1Schema::table('geo', function (Blueprint $table) {2 $table->dropIndex(['state']); // Drops index '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('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();23Schema::disableForeignKeyConstraints();1Schema::enableForeignKeyConstraints();23Schema::disableForeignKeyConstraints();
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。 |