資料庫:遷移
簡介
遷移就像是資料庫的版本控制,允許你的團隊定義和共享應用程式的資料庫模式定義。如果你曾經在從原始碼管理中拉取更改後,還要告訴隊友手動向他們的本地資料庫模式新增列,那麼你已經遇到了資料庫遷移所能解決的問題。
Laravel 的 Schema 門面(facade) 在 Laravel 支援的所有資料庫系統中提供了與資料庫無關的表建立和操作支援。通常,遷移會使用此門面來建立和修改資料庫表和列。
生成遷移
你可以使用 make:migration Artisan 命令 來生成資料庫遷移。新的遷移檔案將被放置在你的 database/migrations 目錄中。每個遷移檔名都包含一個時間戳,允許 Laravel 確定遷移的執行順序。
1php artisan make:migration create_flights_table
Laravel 會使用遷移名稱來嘗試猜測表名,以及該遷移是否正在建立新表。如果 Laravel 能夠從遷移名稱中確定表名,它將預先填充生成的遷移檔案中的指定表。否則,你可以在遷移檔案中手動指定表名。
如果你想為生成的遷移指定自定義路徑,可以在執行 make:migration 命令時使用 --path 選項。給定的路徑應相對於你的應用程式根目錄。
可以使用 存根釋出(stub publishing) 來自定義遷移存根。
壓縮遷移
隨著應用程式的構建,你可能會隨著時間的推移積累越來越多的遷移檔案。這可能會導致你的 database/migrations 目錄變得臃腫,包含數百個遷移檔案。如果你願意,可以將這些遷移“壓縮”成單個 SQL 檔案。要開始使用,請執行 schema:dump 命令。
1php artisan schema:dump2 3# Dump the current database schema and prune all existing migrations...4php artisan schema:dump --prune
執行此命令後,Laravel 會將“模式(schema)”檔案寫入應用程式的 database/schema 目錄。模式檔案的名稱將對應於資料庫連線名稱。現在,當你嘗試遷移資料庫且沒有其他遷移尚未執行時,Laravel 將首先執行你正在使用的資料庫連線的模式檔案中的 SQL 語句。執行完模式檔案的 SQL 語句後,Laravel 將執行任何未包含在模式轉儲中的剩餘遷移。
如果你的應用程式測試使用的資料庫連線與你在本地開發中通常使用的連線不同,請確保你已使用該資料庫連線轉儲了模式檔案,以便你的測試能夠構建資料庫。你可能希望在轉儲本地開發中通常使用的資料庫連線後再執行此操作。
1php artisan schema:dump2php artisan schema:dump --database=testing --prune
你應該將資料庫模式檔案提交到原始碼管理中,以便團隊中的新開發人員能夠快速建立應用程式的初始資料庫結構。
遷移壓縮僅適用於 MariaDB、MySQL、PostgreSQL 和 SQLite 資料庫,並利用了資料庫的命令列客戶端。
遷移結構
遷移類包含兩個方法:up 和 down。up 方法用於向資料庫新增新表、列或索引,而 down 方法應撤銷 up 方法執行的操作。
在這兩個方法中,你都可以使用 Laravel 模式構建器來表達性地建立和修改表。要了解 Schema 構建器上可用的所有方法,請 檢視其文件。例如,以下遷移建立了一個 flights 表:
1<?php 2 3use Illuminate\Database\Migrations\Migration; 4use Illuminate\Database\Schema\Blueprint; 5use Illuminate\Support\Facades\Schema; 6 7return new class extends Migration 8{ 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 }21 22 /**23 * Reverse the migrations.24 */25 public function down(): void26 {27 Schema::drop('flights');28 }29};
設定遷移連線
如果你的遷移將與應用程式預設資料庫連線以外的資料庫連線進行互動,你應該設定遷移的 $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 */11public function up(): void12{13 // ...14}
跳過遷移
有時,某個遷移可能旨在支援尚未啟用的功能,而你不想立即執行它。在這種情況下,你可以在遷移中定義一個 shouldRun 方法。如果 shouldRun 方法返回 false,則會跳過該遷移。
1use App\Models\Flight; 2use Laravel\Pennant\Feature; 3 4/** 5 * Determine if this migration should run. 6 */ 7public function shouldRun(): bool 8{ 9 return Feature::active(Flight::class);10}
執行遷移
要執行所有未執行的遷移,請執行 migrate Artisan 命令。
1php artisan migrate
如果你想檢視哪些遷移已經執行,哪些還在等待中,可以使用 migrate:status Artisan 命令。
1php artisan migrate:status
如果為 migrate 命令提供了 --step 選項,該命令會將每個遷移作為一個單獨的批次執行,從而允許你在稍後使用 migrate:rollback 命令回滾單個遷移。
1php artisan migrate --step
如果你想檢視遷移將要執行的 SQL 語句而不實際執行它們,可以為 migrate 命令提供 --pretend 標誌。
1php artisan migrate --pretend
隔離遷移執行
如果你在多臺伺服器上部署應用程式並將遷移作為部署過程的一部分執行,你可能不希望兩臺伺服器同時嘗試遷移資料庫。為了避免這種情況,可以在呼叫 migrate 命令時使用 isolated 選項。
當提供 isolated 選項時,Laravel 將在嘗試執行遷移之前使用應用程式的快取驅動程式獲取原子鎖。在持有該鎖時,所有其他嘗試執行 migrate 命令的請求都不會執行;不過,命令仍會以成功的退出狀態碼退出。
1php artisan migrate --isolated
要使用此功能,您的應用程式必須使用 memcached、redis、dynamodb、database、file 或 array 快取驅動作為應用程式的預設快取驅動。此外,所有伺服器必須與同一個中央快取伺服器通訊。
強制在生產環境中執行遷移
某些遷移操作是破壞性的,這意味著它們可能會導致資料丟失。為了保護你不在生產資料庫上執行這些命令,在命令執行前會提示確認。要強制在沒有提示的情況下執行命令,請使用 --force 標誌。
1php artisan migrate --force
回滾遷移
要回滾最新的遷移操作,可以使用 rollback Artisan 命令。此命令會回滾最後一“批”遷移,其中可能包含多個遷移檔案。
1php artisan migrate:rollback
你可以透過向 rollback 命令提供 step 選項來回滾有限數量的遷移。例如,以下命令將回滾最近的五個遷移:
1php artisan migrate:rollback --step=5
你可以透過向 rollback 命令提供 batch 選項來回滾特定的“批次”遷移,其中 batch 選項對應於應用程式 migrations 資料表中的批次值。例如,以下命令將回滾第三批中的所有遷移:
1php artisan migrate:rollback --batch=3
如果你想檢視遷移將要執行的 SQL 語句而不實際執行它們,可以為 migrate:rollback 命令提供 --pretend 標誌。
1php artisan migrate:rollback --pretend
migrate:reset 命令將回滾應用程式的所有遷移。
1php artisan migrate:reset
使用單個命令回滾並遷移
migrate:refresh 命令將回滾所有遷移,然後執行 migrate 命令。此命令有效地重新建立了整個資料庫。
1php artisan migrate:refresh2 3# Refresh the database and run all database seeds...4php artisan migrate:refresh --seed
你可以透過向 refresh 命令提供 step 選項來回滾並重新遷移有限數量的遷移。例如,以下命令將回滾並重新遷移最近的五個遷移:
1php artisan migrate:refresh --step=5
刪除所有表並遷移
migrate:fresh 命令將從資料庫中刪除所有表,然後執行 migrate 命令。
1php artisan migrate:fresh2 3php artisan migrate:fresh --seed
預設情況下,migrate:fresh 命令僅刪除預設資料庫連線中的表。但是,你可以使用 --database 選項來指定應遷移的資料庫連線。資料庫連線名稱應對應於應用程式 database 配置檔案 中定義的連線。
1php artisan migrate:fresh --database=admin
migrate:fresh 命令將刪除所有資料庫表,無論它們的字首是什麼。在與其他應用程式共享的資料庫上進行開發時,應謹慎使用此命令。
資料表 (Tables)
建立資料表
要建立新的資料庫表,請在 Schema 門面上使用 create 方法。create 方法接受兩個引數:第一個是表名,第二個是一個閉包,接收一個 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});
在建立表時,你可以使用模式構建器的任何 列方法 來定義表的列。
確定表/列是否存在
你可以使用 hasTable、hasColumn 和 hasIndex 方法來確定表、列或索引是否存在。
1if (Schema::hasTable('users')) { 2 // The "users" table exists... 3} 4 5if (Schema::hasColumn('users', 'email')) { 6 // The "users" table exists and has an "email" column... 7} 8 9if (Schema::hasIndex('users', ['email'], 'unique')) {10 // The "users" table exists and has a unique index on the "email" column...11}
資料庫連線和表選項
如果你想在非預設資料庫連線上執行模式操作,請使用 connection 方法。
1Schema::connection('sqlite')->create('users', function (Blueprint $table) {2 $table->id();3});
此外,還可以使用其他一些屬性和方法來定義表建立的其他方面。在使用 MariaDB 或 MySQL 時,engine 屬性可用於指定表的儲存引擎。
1Schema::create('users', function (Blueprint $table) {2 $table->engine('InnoDB');3 4 // ...5});
在使用 MariaDB 或 MySQL 時,charset 和 collation 屬性可用於指定建立表的字元集和排序規則。
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});
如果你想為資料庫表新增“註釋”,可以呼叫表例項上的 comment 方法。表註釋目前僅由 MariaDB、MySQL 和 PostgreSQL 支援。
1Schema::create('calculations', function (Blueprint $table) {2 $table->comment('Business calculations');3 4 // ...5});
更新資料表
Schema 門面上的 table 方法可用於更新現有表。與 create 方法一樣,table 方法接受兩個引數:表名和一個接收 Blueprint 例項的閉包,你可以使用該例項向表中新增列或索引。
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);
要刪除現有的表,可以使用 drop 或 dropIfExists 方法。
1Schema::drop('users');2 3Schema::dropIfExists('users');
重新命名帶有外部索引鍵的表
在重命名錶之前,你應該確保表上的任何外部索引鍵約束在遷移檔案中都有明確的名稱,而不是讓 Laravel 分配基於約定的名稱。否則,外部索引鍵約束名稱將引用舊的表名。
列
建立列
Schema 門面上的 table 方法可用於更新現有表。與 create 方法一樣,table 方法接受兩個引數:表名和一個接收 Illuminate\Database\Schema\Blueprint 例項的閉包,你可以使用該例項向表中新增列。
1use Illuminate\Database\Schema\Blueprint;2use Illuminate\Support\Facades\Schema;3 4Schema::table('users', function (Blueprint $table) {5 $table->integer('votes');6});
可用列型別
模式構建器藍圖提供了多種方法,對應於你可以新增到資料庫表中的不同型別的列。下表中列出了所有可用的方法。
布林型別
字串與文字型別
數值型別
bigIncrements bigInteger decimal double float id increments integer mediumIncrements mediumInteger smallIncrements smallInteger tinyIncrements tinyInteger unsignedBigInteger unsignedInteger unsignedMediumInteger unsignedSmallInteger unsignedTinyInteger
日期與時間型別
dateTime dateTimeTz date time timeTz timestamp timestamps timestampsTz softDeletes softDeletesTz year
二進位制型別
物件與 JSON 型別
UUID 與 ULID 型別
空間型別
關係型別
特殊型別
bigIncrements()
bigIncrements 方法建立一個自動遞增的 UNSIGNED BIGINT(主鍵)等效列。
1$table->bigIncrements('id');
bigInteger()
bigInteger 方法建立一個 BIGINT 等效列。
1$table->bigInteger('votes');
binary()
binary 方法建立一個 BLOB 等效列。
1$table->binary('photo');
在使用 MySQL、MariaDB 或 SQL Server 時,你可以傳遞 length 和 fixed 引數來建立 VARBINARY 或 BINARY 等效列。
1$table->binary('data', length: 16); // VARBINARY(16)2 3$table->binary('data', length: 16, fixed: true); // BINARY(16)
boolean()
boolean 方法建立一個 BOOLEAN 等效列。
1$table->boolean('confirmed');
char()
char 方法建立一個具有指定長度的 CHAR 等效列。
1$table->char('name', length: 100);
dateTimeTz()
dateTimeTz 方法建立一個 DATETIME(帶時區)等效列,並帶有可選的分秒精度。
1$table->dateTimeTz('created_at', precision: 0);
dateTime()
dateTime 方法建立一個 DATETIME 等效列,並帶有可選的分秒精度。
1$table->dateTime('created_at', precision: 0);
date()
date 方法建立一個 DATE 等效列。
1$table->date('created_at');
decimal()
decimal 方法建立一個具有指定精度(總位數)和標度(小數點位數)的 DECIMAL 等效列。
1$table->decimal('amount', total: 8, places: 2);
double()
double 方法建立一個 DOUBLE 等效列。
1$table->double('amount');
enum()
enum 方法建立一個具有給定有效值的 ENUM 等效列。
1$table->enum('difficulty', ['easy', 'hard']);
當然,你可以使用 Enum::cases() 方法而不是手動定義允許值的陣列。
1use App\Enums\Difficulty;2 3$table->enum('difficulty', Difficulty::cases());
float()
float 方法建立一個具有指定精度的 FLOAT 等效列。
1$table->float('amount', precision: 53);
foreignId()
foreignId 方法建立一個 UNSIGNED BIGINT 等效列。
1$table->foreignId('user_id');
foreignIdFor()
foreignIdFor 方法為給定的模型類新增一個 {column}_id 等效列。根據模型鍵型別的不同,列型別將為 UNSIGNED BIGINT、CHAR(36) 或 CHAR(26)。
1$table->foreignIdFor(User::class);
foreignUlid()
foreignUlid 方法建立一個 ULID 等效列。
1$table->foreignUlid('user_id');
foreignUuid()
foreignUuid 方法建立一個 UUID 等效列。
1$table->foreignUuid('user_id');
geography()
geography 方法建立一個具有給定空間型別和 SRID(空間參考系統識別符號)的 GEOGRAPHY 等效列。
1$table->geography('coordinates', subtype: 'point', srid: 4326);
空間型別的支援取決於你的資料庫驅動程式。請參考你的資料庫文件。如果你的應用程式使用的是 PostgreSQL 資料庫,則必須先安裝 PostGIS 擴充套件,然後才能使用 geography 方法。
geometry()
geometry 方法建立一個具有給定空間型別和 SRID(空間參考系統識別符號)的 GEOMETRY 等效列。
1$table->geometry('positions', subtype: 'point', srid: 0);
空間型別的支援取決於你的資料庫驅動程式。請參考你的資料庫文件。如果你的應用程式使用的是 PostgreSQL 資料庫,則必須先安裝 PostGIS 擴充套件,然後才能使用 geometry 方法。
id()
id 方法是 bigIncrements 方法的別名。預設情況下,該方法將建立一個 id 列;但是,如果你想為該列指定不同的名稱,可以傳遞一個列名。
1$table->id();
increments()
increments 方法建立一個自動遞增的 UNSIGNED INTEGER 等效列作為主鍵。
1$table->increments('id');
integer()
integer 方法建立一個 INTEGER 等效列。
1$table->integer('votes');
ipAddress()
ipAddress 方法建立一個 VARCHAR 等效列。
1$table->ipAddress('visitor');
使用 PostgreSQL 時,將建立一個 INET 列。
json()
json 方法建立一個 JSON 等效列。
1$table->json('options');
使用 SQLite 時,將建立一個 TEXT 列。
jsonb()
jsonb 方法建立一個 JSONB 等效列。
1$table->jsonb('options');
使用 SQLite 時,將建立一個 TEXT 列。
longText()
longText 方法建立一個 LONGTEXT 等效列。
1$table->longText('description');
在使用 MySQL 或 MariaDB 時,你可以為該列應用 binary 字元集,以建立 LONGBLOB 等效列。
1$table->longText('data')->charset('binary'); // LONGBLOB
macAddress()
macAddress 方法建立一個旨在儲存 MAC 地址的列。某些資料庫系統(例如 PostgreSQL)具有專門針對此類資料的列型別。其他資料庫系統將使用字串等效列。
1$table->macAddress('device');
mediumIncrements()
mediumIncrements 方法建立一個自動遞增的 UNSIGNED MEDIUMINT 等效列作為主鍵。
1$table->mediumIncrements('id');
mediumInteger()
mediumInteger 方法建立一個 MEDIUMINT 等效列。
1$table->mediumInteger('votes');
mediumText()
mediumText 方法建立一個 MEDIUMTEXT 等效列。
1$table->mediumText('description');
在使用 MySQL 或 MariaDB 時,你可以為該列應用 binary 字元集,以建立 MEDIUMBLOB 等效列。
1$table->mediumText('data')->charset('binary'); // MEDIUMBLOB
morphs()
morphs 方法是一個便捷方法,它新增一個 {column}_id 等效列和一個 {column}_type VARCHAR 等效列。{column}_id 的列型別將根據模型鍵型別的不同而為 UNSIGNED BIGINT、CHAR(36) 或 CHAR(26)。
此方法旨在定義多型 Eloquent 關係 所需的列。在以下示例中,將建立 taggable_id 和 taggable_type 列:
1$table->morphs('taggable');
nullableMorphs()
該方法類似於 morphs 方法;但是,所建立的列將是“可為空的”(nullable)。
1$table->nullableMorphs('taggable');
nullableUlidMorphs()
該方法類似於 ulidMorphs 方法;但是,所建立的列將是“可為空的”。
1$table->nullableUlidMorphs('taggable');
nullableUuidMorphs()
該方法類似於 uuidMorphs 方法;但是,所建立的列將是“可為空的”。
1$table->nullableUuidMorphs('taggable');
rememberToken()
rememberToken 方法建立一個可為空的 VARCHAR(100) 等效列,旨在儲存當前的“記住我” 身份驗證令牌。
1$table->rememberToken();
set()
set 方法建立一個具有給定有效值列表的 SET 等效列。
1$table->set('flavors', ['strawberry', 'vanilla']);
smallIncrements()
smallIncrements 方法建立一個自動遞增的 UNSIGNED SMALLINT 等效列作為主鍵。
1$table->smallIncrements('id');
smallInteger()
smallInteger 方法建立一個 SMALLINT 等效列。
1$table->smallInteger('votes');
softDeletesTz()
softDeletesTz 方法新增一個可為空的 deleted_at TIMESTAMP(帶時區)等效列,並帶有可選的分秒精度。該列旨在儲存 Eloquent “軟刪除”功能所需的 deleted_at 時間戳。
1$table->softDeletesTz('deleted_at', precision: 0);
softDeletes()
softDeletes 方法新增一個可為空的 deleted_at TIMESTAMP 等效列,並帶有可選的分秒精度。該列旨在儲存 Eloquent “軟刪除”功能所需的 deleted_at 時間戳。
1$table->softDeletes('deleted_at', precision: 0);
string()
string 方法建立一個指定長度的 VARCHAR 等效列。
1$table->string('name', length: 100);
text()
text 方法建立一個 TEXT 等效列。
1$table->text('description');
在使用 MySQL 或 MariaDB 時,你可以為該列應用 binary 字元集,以建立 BLOB 等效列。
1$table->text('data')->charset('binary'); // BLOB
timeTz()
timeTz 方法建立一個 TIME(帶時區)等效列,並帶有可選的分秒精度。
1$table->timeTz('sunrise', precision: 0);
time()
time 方法建立一個 TIME 等效列,並帶有可選的分秒精度。
1$table->time('sunrise', precision: 0);
timestampTz()
timestampTz 方法建立一個 TIMESTAMP(帶時區)等效列,並帶有可選的分秒精度。
1$table->timestampTz('added_at', precision: 0);
timestamp()
timestamp 方法建立一個 TIMESTAMP 等效列,並帶有可選的分秒精度。
1$table->timestamp('added_at', precision: 0);
timestampsTz()
timestampsTz 方法建立 created_at 和 updated_at TIMESTAMP(帶時區)等效列,並帶有可選的分秒精度。
1$table->timestampsTz(precision: 0);
timestamps()
timestamps 方法建立 created_at 和 updated_at TIMESTAMP 等效列,並帶有可選的分秒精度。
1$table->timestamps(precision: 0);
tinyIncrements()
tinyIncrements 方法建立一個自動遞增的 UNSIGNED TINYINT 等效列作為主鍵。
1$table->tinyIncrements('id');
tinyInteger()
tinyInteger 方法建立一個 TINYINT 等效列。
1$table->tinyInteger('votes');
tinyText()
tinyText 方法建立一個 TINYTEXT 等效列。
1$table->tinyText('notes');
在使用 MySQL 或 MariaDB 時,你可以為該列應用 binary 字元集,以建立 TINYBLOB 等效列。
1$table->tinyText('data')->charset('binary'); // TINYBLOB
unsignedBigInteger()
unsignedBigInteger 方法建立一個 UNSIGNED BIGINT 等效列。
1$table->unsignedBigInteger('votes');
unsignedInteger()
unsignedInteger 方法建立一個 UNSIGNED INTEGER 等效列。
1$table->unsignedInteger('votes');
unsignedMediumInteger()
unsignedMediumInteger 方法建立一個 UNSIGNED MEDIUMINT 等效列。
1$table->unsignedMediumInteger('votes');
unsignedSmallInteger()
unsignedSmallInteger 方法建立一個 UNSIGNED SMALLINT 等效列。
1$table->unsignedSmallInteger('votes');
unsignedTinyInteger()
unsignedTinyInteger 方法建立一個 UNSIGNED TINYINT 等效列。
1$table->unsignedTinyInteger('votes');
ulidMorphs()
ulidMorphs 方法是一個便捷方法,它新增一個 {column}_id CHAR(26) 等效列和一個 {column}_type VARCHAR 等效列。
此方法旨在定義使用 ULID 識別符號的多型 Eloquent 關係 所需的列。在以下示例中,將建立 taggable_id 和 taggable_type 列:
1$table->ulidMorphs('taggable');
uuidMorphs()
uuidMorphs 方法是一個便捷方法,它新增一個 {column}_id CHAR(36) 等效列和一個 {column}_type VARCHAR 等效列。
此方法旨在定義使用 UUID 識別符號的 多型 Eloquent 關係 所需的列。在以下示例中,將建立 taggable_id 和 taggable_type 列:
1$table->uuidMorphs('taggable');
ulid()
ulid 方法建立一個 ULID 等效列。
1$table->ulid('id');
uuid()
uuid 方法建立一個 UUID 等效列。
1$table->uuid('id');
vector()
vector 方法建立一個 vector 等效列。
1$table->vector('embedding', dimensions: 100);
在使用 PostgreSQL 時,必須先載入 pgvector 擴充套件,然後才能建立 vector 列。
1Schema::ensureVectorExtensionExists();
year()
year 方法建立一個 YEAR 等效列。
1$table->year('birth_year');
列修飾符
除了上面列出的列型別外,當你向資料庫表中新增列時,還可以使用幾種“列修飾符”。例如,要使列“可為空”,可以使用 nullable 方法。
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') |
將列放置在另一個列“之後”(MariaDB / MySQL)。 |
->autoIncrement() |
將 INTEGER 列設定為自動遞增(主鍵)。 |
->charset('utf8mb4') |
為列指定字元集(MariaDB / MySQL)。 |
->collation('utf8mb4_unicode_ci') |
為列指定排序規則。 |
->comment('my comment') |
為列添加註釋(MariaDB / MySQL / PostgreSQL)。 |
->default($value) |
為列指定“預設”值。 |
->first() |
將列放置在表中的“第一位”(MariaDB / MySQL)。 |
->from($integer) |
設定自動遞增欄位的起始值(MariaDB / MySQL / PostgreSQL)。 |
->instant() |
使用即時操作新增或修改列(MySQL)。 |
->invisible() |
使列對 SELECT * 查詢“不可見”(MariaDB / MySQL)。 |
->lock($mode) |
為列操作指定鎖定模式(MySQL)。 |
->nullable($value = true) |
允許將 NULL 值插入到該列中。 |
->storedAs($expression) |
建立儲存的生成列(MariaDB / MySQL / PostgreSQL / SQLite)。 |
->unsigned() |
將 INTEGER 列設定為 UNSIGNED(MariaDB / MySQL)。 |
->useCurrent() |
將 TIMESTAMP 列設定為使用 CURRENT_TIMESTAMP 作為預設值。 |
->useCurrentOnUpdate() |
將 TIMESTAMP 列設定為在記錄更新時使用 CURRENT_TIMESTAMP(MariaDB / MySQL)。 |
->virtualAs($expression) |
建立虛擬生成列(MariaDB / MySQL / SQLite)。 |
->generatedAs($expression) |
建立具有指定序列選項的標識列(PostgreSQL)。 |
->always() |
為標識列定義序列值優於輸入值的優先順序(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 8return new class extends Migration 9{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};
預設表示式的支援取決於你的資料庫驅動程式、資料庫版本和欄位型別。請參考你的資料庫文件。
列順序
在使用 MariaDB 或 MySQL 資料庫時,after 方法可用於在模式中的現有列之後新增列。
1$table->after('password', function (Blueprint $table) {2 $table->string('address_line1');3 $table->string('address_line2');4 $table->string('city');5});
即時列操作
在使用 MySQL 時,你可以將 instant 修飾符連結到列定義上,以指示應使用 MySQL 的“即時”演算法來新增或修改該列。此演算法允許在不進行完整表重建的情況下執行某些模式更改,從而使它們幾乎是瞬時的,無論表的大小如何。
1$table->string('name')->nullable()->instant();
即時列新增只能將列追加到表的末尾,因此 instant 修飾符不能與 after 或 first 修飾符結合使用。此外,該演算法不支援所有列型別或操作。如果請求的操作不相容,MySQL 將引發錯誤。
請參考 MySQL 文件 以確定哪些操作與即時列修改相容。
DDL 鎖定
在使用 MySQL 時,你可以將 lock 修飾符連結到列、索引或外部索引鍵定義上,以控制模式操作期間的表鎖定。MySQL 支援幾種鎖定模式:none 允許併發讀寫,shared 允許併發讀取但阻止寫入,exclusive 阻止所有併發訪問,default 讓 MySQL 選擇最合適的模式。
1$table->string('name')->lock('none');2 3$table->index('email')->lock('shared');
如果請求的鎖定模式與操作不相容,MySQL 將引發錯誤。lock 修飾符可以與 instant 修飾符結合使用,以進一步最佳化模式更改。
1$table->string('name')->instant()->lock('none');
修改列
change 方法允許你修改現有列的型別和屬性。例如,你可能希望增加 string 列的大小。要檢視 change 方法的實際效果,讓我們將 name 列的大小從 25 增加到 50。為此,我們只需定義列的新狀態,然後呼叫 change 方法。
1Schema::table('users', function (Blueprint $table) {2 $table->string('name', 50)->change();3});
修改列時,必須顯式包含要保留在列定義上的所有修飾符——任何缺失的屬性都將被刪除。例如,要保留 unsigned、default 和 comment 屬性,你必須在更改列時顯式呼叫每個修飾符。
1Schema::table('users', function (Blueprint $table) {2 $table->integer('votes')->unsigned()->default(1)->comment('my comment')->change();3});
change 方法不會更改列的索引。因此,在修改列時,你可以使用索引修飾符來顯式新增或刪除索引。
1// Add an index...2$table->bigIncrements('id')->primary()->change();3 4// Drop an index...5$table->char('postal_code', 10)->unique(false)->change();
重新命名列
要重新命名列,可以使用模式構建器提供的 renameColumn 方法。
1Schema::table('users', function (Blueprint $table) {2 $table->renameColumn('from', 'to');3});
刪除列
要刪除列,可以使用模式構建器上的 dropColumn 方法。
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});
可用的命令別名
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 模式構建器支援多種型別的索引。以下示例建立了一個新的 email 列,並指定其值應該是唯一的。為了建立索引,我們可以將 unique 方法連結到列定義上。
1use Illuminate\Database\Schema\Blueprint;2use Illuminate\Support\Facades\Schema;3 4Schema::table('users', function (Blueprint $table) {5 $table->string('email')->unique();6});
或者,你也可以在定義列後建立索引。為此,你應該在模式構建器藍圖上呼叫 unique 方法。此方法接受應接收唯一索引的列名。
1$table->unique('email');
你甚至可以將列陣列傳遞給索引方法,以建立複合索引。
1$table->index(['account_id', 'created_at']);
建立索引時,Laravel 會自動根據表名、列名和索引型別生成索引名稱,但你可以將第二個引數傳遞給該方法以自行指定索引名稱。
1$table->unique('email', 'unique_email');
可用的索引型別
Laravel 的模式構建器藍圖類提供了用於建立 Laravel 支援的每種型別索引的方法。每個索引方法都接受一個可選的第二個引數來指定索引名稱。如果省略,名稱將根據用於索引的表名、列名以及索引型別推匯出來。下表中描述了每個可用的索引方法。
| 命令 | 描述 |
|---|---|
$table->primary('id'); |
新增主鍵。 |
$table->primary(['id', 'parent_id']); |
新增複合鍵。 |
$table->unique('email'); |
新增唯一索引。 |
$table->index('state'); |
新增普通索引。 |
$table->fullText('body'); |
新增全文索引(MariaDB / MySQL / PostgreSQL)。 |
$table->fullText('body')->language('english'); |
新增指定語言的全文索引(PostgreSQL)。 |
$table->spatialIndex('location'); |
新增空間索引(SQLite 除外)。 |
線上索引建立
預設情況下,在大表上建立索引可能會鎖定表,並在構建索引時阻止讀取或寫入。在使用 PostgreSQL 或 SQL Server 時,你可以將 online 方法連結到索引定義上,以在不鎖定表的情況下建立索引,從而允許你的應用程式在索引建立期間繼續讀取和寫入資料。
1$table->string('email')->unique()->online();
使用 PostgreSQL 時,這會為索引建立語句新增 CONCURRENTLY 選項。使用 SQL Server 時,這會新增 WITH (online = on) 選項。
重新命名索引
要重新命名索引,可以使用模式構建器藍圖提供的 renameIndex 方法。此方法接受當前索引名稱作為第一個引數,接受期望的名稱作為第二個引數。
1$table->renameIndex('from', 'to')
刪除索引
要刪除索引,必須指定索引的名稱。預設情況下,Laravel 會自動根據表名、索引列的名稱和索引型別分配索引名稱。以下是一些示例:
| 命令 | 描述 |
|---|---|
$table->dropPrimary('users_id_primary'); |
從“users”表中刪除主鍵。 |
$table->dropUnique('users_email_unique'); |
從“users”表中刪除唯一索引。 |
$table->dropIndex('geo_state_index'); |
從“geo”表中刪除普通索引。 |
$table->dropFullText('posts_body_fulltext'); |
從“posts”表中刪除全文索引。 |
$table->dropSpatialIndex('geo_location_spatialindex'); |
從“geo”表中刪除空間索引(SQLite 除外)。 |
如果你將列陣列傳遞給刪除索引的方法,常規索引名稱將根據表名、列和索引型別生成。
1Schema::table('geo', function (Blueprint $table) {2 $table->dropIndex(['state']); // Drops index 'geo_state_index'3});
外部索引鍵約束
Laravel 還支援建立外部索引鍵約束,這些約束用於在資料庫級別強制實施引用完整性。例如,讓我們在 posts 表上定義一個 user_id 列,該列引用 users 表上的 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});
由於這種語法比較冗長,Laravel 提供了額外的、更簡潔的方法,利用約定來提供更好的開發體驗。使用 foreignId 方法建立列時,上面的示例可以改寫如下:
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});
你還可以為約束的“刪除時”和“更新時”屬性指定期望的操作。
1$table->foreignId('user_id')2 ->constrained()3 ->onUpdate('cascade')4 ->onDelete('cascade');
還提供了這些操作的另一種表達性語法。
| 方法 | 描述 |
|---|---|
$table->cascadeOnUpdate(); |
更新應級聯。 |
$table->restrictOnUpdate(); |
更新應受到限制。 |
$table->nullOnUpdate(); |
更新應將外部索引鍵值設定為 null。 |
$table->noActionOnUpdate(); |
更新時無操作。 |
$table->cascadeOnDelete(); |
刪除應級聯。 |
$table->restrictOnDelete(); |
刪除應受到限制。 |
$table->nullOnDelete(); |
刪除應將外部索引鍵值設定為 null。 |
$table->noActionOnDelete(); |
如果存在子記錄,則阻止刪除。 |
任何額外的 列修飾符 都必須在 constrained 方法之前呼叫。
1$table->foreignId('user_id')2 ->nullable()3 ->constrained();
刪除外部索引鍵
要刪除外部索引鍵,可以使用 dropForeign 方法,並將要刪除的外部索引鍵約束名稱作為引數傳遞。外部索引鍵約束使用與索引相同的命名約定。換句話說,外部索引鍵約束名稱基於表名和約束中的列名,並帶有“_foreign”字尾。
1$table->dropForeign('posts_user_id_foreign');
或者,你可以將包含持有外部索引鍵的列名的陣列傳遞給 dropForeign 方法。該陣列將根據 Laravel 的約束命名約定轉換為外部索引鍵約束名稱。
1$table->dropForeign(['user_id']);
切換外部索引鍵約束
你可以使用以下方法在遷移中啟用或停用外部索引鍵約束。
1Schema::enableForeignKeyConstraints();2 3Schema::disableForeignKeyConstraints();4 5Schema::withoutForeignKeyConstraints(function () {6 // Constraints disabled within this closure...7});
SQLite 預設停用外部索引鍵約束。在使用 SQLite 時,請確保在嘗試在遷移中建立外部索引鍵之前,在你的資料庫配置中 啟用外部索引鍵支援。
活動
為方便起見,每個遷移操作都會排程一個 事件。以下所有事件都擴充套件了基礎 Illuminate\Database\Events\MigrationEvent 類。
| 類 | 描述 |
|---|---|
Illuminate\Database\Events\MigrationsStarted |
即將執行一批遷移。 |
Illuminate\Database\Events\MigrationsEnded |
一批遷移已執行完畢。 |
Illuminate\Database\Events\MigrationStarted |
即將執行單個遷移。 |
Illuminate\Database\Events\MigrationEnded |
單個遷移已執行完畢。 |
Illuminate\Database\Events\NoPendingMigrations |
遷移命令未發現待處理的遷移。 |
Illuminate\Database\Events\SchemaDumped |
資料庫模式轉儲已完成。 |
Illuminate\Database\Events\SchemaLoaded |
現有的資料庫模式轉儲已載入。 |