資料庫:遷移
簡介
遷移就像是資料庫的版本控制,讓您的團隊能夠定義和共享應用程式的資料庫架構定義。如果您曾經不得不告訴隊友從源代碼控制中拉取您的更改後,手動將一個欄位添加到他們的本地資料庫架構中,那麼您已經遇到了遷移解決的問題。
Laravel Schema
Facades 提供了跨所有 Laravel 支持的資料庫系統創建和操作表的支持。通常,遷移將使用這個 Facade 來創建和修改資料庫表和欄位。
生成遷移
您可以使用 make:migration
Artisan 指令 來生成一個資料庫遷移。新的遷移將放置在您的 database/migrations
目錄中。每個遷移文件名都包含一個時間戳,這使 Laravel 能夠確定遷移的順序:
php artisan make:migration create_flights_table
Laravel 將使用遷移的名稱來嘗試猜測表的名稱以及遷移是否將建立新表。如果 Laravel 能夠從遷移名稱中確定表名,Laravel 將使用指定的表預先填充生成的遷移文件。否則,您可以在遷移文件中手動指定表。
如果您想為生成的遷移指定自定義路徑,您可以在執行 make:migration
命令時使用 --path
選項。給定的路徑應該相對於您應用程序的基本路徑。
[!NOTE]
遷移樣板可以使用 樣板發布 進行自定義。
合併遷移
隨著應用程序的構建,您可能會隨著時間累積越來越多的遷移。這可能導致您的 database/migrations
目錄中積累了數百個遷移。如果您希望,您可以將您的遷移“合併”為單個 SQL 文件。要開始,執行 schema:dump
命令:
php artisan schema:dump
# Dump the current database schema and prune all existing migrations...
php artisan schema:dump --prune
當您執行此命令時,Laravel 將在您應用程序的 database/schema
目錄中寫入一個“schema”文件。模式文件的名稱將對應到數據庫連接。現在,當您嘗試遷移數據庫且沒有執行其他遷移時,Laravel 將首先執行數據庫連接的模式文件中的 SQL 語句。執行模式文件的 SQL 語句後,Laravel 將執行未包含在模式轉儲中的任何剩餘遷移。
如果您的應用程序測試使用與您在本地開發期間通常使用的不同數據庫連接,您應確保已使用該數據庫連接轉儲了模式文件,以便您的測試能夠構建您的數據庫。您可能希望在轉儲通常在本地開發期間使用的數據庫連接後執行此操作:
php artisan schema:dump
php artisan schema:dump --database=testing --prune
你應該將你的資料庫結構檔案提交到源代碼控制,這樣你團隊中的新開發人員可以快速建立應用程式的初始資料庫結構。
[!WARNING]
遷移壓縮僅適用於 MariaDB、MySQL、PostgreSQL 和 SQLite 資料庫,並使用資料庫的命令列客戶端。
遷移結構
一個遷移類別包含兩個方法:up
和 down
。up
方法用於向資料庫新增新的表格、欄位或索引,而 down
方法應該撤銷 up
方法執行的操作。
在這兩個方法中,你可以使用 Laravel 結構建立器來表達性地創建和修改表格。要了解 Schema
建立器上所有可用的方法,請查看其文件。例如,以下遷移創建一個 flights
表格:
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('flights', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('airline');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::drop('flights');
}
};
設置遷移連線
如果你的遷移將與應用程式的預設資料庫連線以外的資料庫連線進行交互,你應該設置遷移的 $connection
屬性:
/**
* The database connection that should be used by the migration.
*
* @var string
*/
protected $connection = 'pgsql';
/**
* Run the migrations.
*/
public function up(): void
{
// ...
}
執行遷移
要執行所有未完成的遷移,執行 migrate
Artisan 指令:
php artisan migrate
如果你想查看到目前為止已執行的遷移,你可以使用 migrate:status
Artisan 指令:
php artisan migrate:status
如果你想查看將由遷移執行的 SQL 陳述,而不實際執行它們,你可以為 migrate
指令提供 --pretend
標誌:
php artisan migrate --pretend
隔離遷移執行
如果你正在跨多個伺服器部署應用程式並將遷移作為部署流程的一部分,你可能不希望兩個伺服器同時嘗試遷移資料庫。為了避免這種情況,當調用 migrate
指令時,你可以使用 isolated
選項。
當提供 isolated
選項時,Laravel 將在嘗試運行遷移之前使用應用程式的快取驅動程式獲取原子鎖定。在持有該鎖定時,所有其他嘗試運行 migrate
命令的操作將不會執行;但是,該命令仍將以成功的退出狀態碼退出:
php artisan migrate --isolated
[!WARNING]
為了使用此功能,您的應用程式必須使用memcached
、redis
、dynamodb
、database
、file
或array
快取驅動程式作為應用程式的默認快取驅動程式。此外,所有伺服器必須與同一中央快取伺服器通訊。
強制在正式環境中運行遷移
某些遷移操作是具有破壞性的,這意味著可能會導致數據丟失。為了防止您對正式資料庫運行這些命令,將在執行命令之前提示您進行確認。要強制運行命令而不提示,請使用 --force
標誌:
php artisan migrate --force
回滾遷移
要回滾最新的遷移操作,您可以使用 rollback
Artisan 命令。此命令將回滾最後一個 "批次" 的遷移,這可能包括多個遷移文件:
php artisan migrate:rollback
您可以通過為 rollback
命令提供 step
選項來回滾有限數量的遷移。例如,以下命令將回滾最後五個遷移:
php artisan migrate:rollback --step=5
您可以通過為 rollback
命令提供 batch
選項來回滾特定的 "批次" 遷移,其中 batch
選項對應於應用程式的 migrations
資料庫表中的批次值。例如,以下命令將回滾第三批次中的所有遷移:
php artisan migrate:rollback --batch=3
如果您想查看將由遷移執行的 SQL 語句,而不實際運行它們,您可以為 migrate:rollback
命令提供 --pretend
標誌:
php artisan migrate:rollback --pretend
migrate:reset
指令將會還原應用程式的所有遷移:
php artisan migrate:reset
使用單一指令還原和遷移
migrate:refresh
指令將會還原所有遷移,然後執行 migrate
指令。這個指令有效地重新建立整個資料庫:
php artisan migrate:refresh
# Refresh the database and run all database seeds...
php artisan migrate:refresh --seed
您可以透過為 refresh
指令提供 step
選項,來還原和重新遷移有限數量的遷移。例如,以下指令將會還原和重新遷移最後五個遷移:
php artisan migrate:refresh --step=5
刪除所有資料表並遷移
migrate:fresh
指令將會刪除資料庫中的所有資料表,然後執行 migrate
指令:
php artisan migrate:fresh
php artisan migrate:fresh --seed
預設情況下,migrate:fresh
指令僅會從預設資料庫連線中刪除資料表。但是,您可以使用 --database
選項來指定應該遷移的資料庫連線。資料庫連線名稱應該對應到應用程式的 database
組態檔案 中定義的連線:
php artisan migrate:fresh --database=admin
[!WARNING]
migrate:fresh
指令將會刪除所有資料庫資料表,不論其前綴為何。在與其他應用程式共享的資料庫上開發時,應謹慎使用此指令。
資料表
建立資料表
要建立新的資料庫資料表,請在 Schema
Facade 上使用 create
方法。create
方法接受兩個引數:第一個是資料表的名稱,第二個是一個接收 Blueprint
物件的閉包,可用於定義新資料表:
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('email');
$table->timestamps();
});
在建立資料表時,您可以使用 schema builder 的任何 column methods 來定義資料表的欄位。
確定表格/欄位存在性
您可以使用 hasTable
、hasColumn
和 hasIndex
方法來確定表格、欄位或索引是否存在:
if (Schema::hasTable('users')) {
// The "users" table exists...
}
if (Schema::hasColumn('users', 'email')) {
// The "users" table exists and has an "email" column...
}
if (Schema::hasIndex('users', ['email'], 'unique')) {
// The "users" table exists and has a unique index on the "email" column...
}
資料庫連線和表格選項
如果您想對非應用程式預設連線的資料庫連線執行結構操作,請使用 connection
方法:
Schema::connection('sqlite')->create('users', function (Blueprint $table) {
$table->id();
});
此外,還可以使用一些其他屬性和方法來定義表格建立的其他方面。當使用 MariaDB 或 MySQL 時,可以使用 engine
屬性來指定表格的儲存引擎:
Schema::create('users', function (Blueprint $table) {
$table->engine('InnoDB');
// ...
});
charset
和 collation
屬性可用於指定在使用 MariaDB 或 MySQL 時為建立的表格指定字元集和校對:
Schema::create('users', function (Blueprint $table) {
$table->charset('utf8mb4');
$table->collation('utf8mb4_unicode_ci');
// ...
});
temporary
方法可用於指示表格應該是「臨時」的。臨時表格僅對當前連線的資料庫會話可見,並在連線關閉時自動刪除:
Schema::create('calculations', function (Blueprint $table) {
$table->temporary();
// ...
});
如果您想要為資料庫表格添加「註解」,您可以在表格實例上調用 comment
方法。目前僅支援 MariaDB、MySQL 和 PostgreSQL 的表格註解:
Schema::create('calculations', function (Blueprint $table) {
$table->comment('Business calculations');
// ...
});
更新表格
Schema
門面上的 table
方法可用於更新現有表格。與 create
方法一樣,table
方法接受兩個引數:表格名稱和一個接收 Blueprint
實例的閉包,您可以使用該實例向表格添加欄位或索引:
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
Schema::table('users', function (Blueprint $table) {
$table->integer('votes');
});
重新命名/刪除表格
要重新命名現有的資料庫表格,請使用 rename
方法:
use Illuminate\Support\Facades\Schema;
Schema::rename($from, $to);
要刪除現有資料表,您可以使用 drop
或 dropIfExists
方法:
Schema::drop('users');
Schema::dropIfExists('users');
使用外鍵重新命名資料表
在重新命名資料表之前,您應該驗證資料表上的任何外鍵約束在您的遷移檔案中是否有明確的名稱,而不是讓 Laravel 分配基於慣例的名稱。否則,外鍵約束名稱將參考舊資料表名稱。
欄位
創建欄位
Schema
門面上的 table
方法可用於更新現有資料表。與 create
方法一樣,table
方法接受兩個引數:資料表名稱和一個接收 Illuminate\Database\Schema\Blueprint
實例的閉包,您可以使用該實例向資料表添加欄位:
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
Schema::table('users', function (Blueprint $table) {
$table->integer('votes');
});
可用的欄位類型
結構生成器藍圖提供了各種方法,對應於您可以添加到資料庫表中的不同類型的欄位。下表列出了每個可用方法:
布林類型
字串和文字類型
char longText mediumText string text tinyText
數值類型
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 類型
ulid ulidMorphs uuid uuidMorphs nullableUlidMorphs nullableUuidMorphs
空間類型
關係類型
專用類型
bigIncrements()
bigIncrements
方法創建一個自動遞增的 UNSIGNED BIGINT
(主鍵)等效列:
$table->bigIncrements('id');
bigInteger()
bigInteger
方法創建一個 BIGINT
等效列:
$table->bigInteger('votes');
binary()
binary
方法創建一個 BLOB
等效列:
$table->binary('photo');
在使用 MySQL、MariaDB 或 SQL Server 時,您可以傳遞 length
和 fixed
參數來創建 VARBINARY
或 BINARY
等效列:
$table->binary('data', length: 16); // VARBINARY(16)
$table->binary('data', length: 16, fixed: true); // BINARY(16)
boolean()
boolean
方法創建一個 BOOLEAN
等效列:
$table->boolean('confirmed');
char()
char
方法創建一個指定長度的 CHAR
等效列:
$table->char('name', length: 100);
dateTimeTz()
dateTimeTz
方法創建具有可選小數秒精度的帶時區的 DATETIME
等效列:
$table->dateTimeTz('created_at', precision: 0);
dateTime()
dateTime
方法創建具有可選小數秒精度的 DATETIME
等效列:
$table->dateTime('created_at', precision: 0);
date()
date
方法創建 DATE
等效列:
$table->date('created_at');
decimal()
decimal
方法創建具有給定精度(總位數)和比例(小數位數)的 DECIMAL
等效列:
$table->decimal('amount', total: 8, places: 2);
double()
double
方法創建 DOUBLE
等效列:
$table->double('amount');
enum()
enum
方法創建具有給定有效值的 ENUM
等效列:
$table->enum('difficulty', ['easy', 'hard']);
float()
float
方法創建具有給定精度的 FLOAT
等效列:
$table->float('amount', precision: 53);
foreignId()
foreignId
方法創建一個 UNSIGNED BIGINT
等效列:
$table->foreignId('user_id');
foreignIdFor()
foreignIdFor
方法為給定的模型類添加一個 {column}_id
等效列。該列類型將是 UNSIGNED BIGINT
、CHAR(36)
或 CHAR(26)
,具體取決於模型鍵類型:
$table->foreignIdFor(User::class);
foreignUlid()
foreignUlid
方法創建一個等效的 ULID
欄位:
$table->foreignUlid('user_id');
foreignUuid()
foreignUuid
方法創建一個等效的 UUID
欄位:
$table->foreignUuid('user_id');
geography()
geography
方法使用給定的空間類型和 SRID(空間參考系統標識符)創建一個等效的 GEOGRAPHY
欄位:
$table->geography('coordinates', subtype: 'point', srid: 4326);
> [!NOTE]
> 空間類型的支援取決於您的資料庫驅動程式。請參考您資料庫的文件。如果您的應用程式使用 PostgreSQL 資料庫,您必須在使用 geography
方法之前安裝 PostGIS 擴充功能。
geometry()
geometry
方法使用給定的空間類型和 SRID(空間參考系統標識符)創建一個等效的 GEOMETRY
欄位:
$table->geometry('positions', subtype: 'point', srid: 0);
> [!NOTE]
> 空間類型的支援取決於您的資料庫驅動程式。請參考您資料庫的文件。如果您的應用程式使用 PostgreSQL 資料庫,您必須在使用 geometry
方法之前安裝 PostGIS 擴充功能。
id()
id
方法是 bigIncrements
方法的別名。預設情況下,該方法將創建一個 id
欄位;但是,如果您想要為欄位指定不同的名稱,則可以傳遞一個欄位名稱:
$table->id();
increments()
increments
方法創建一個自動遞增的 UNSIGNED INTEGER
等效欄位作為主鍵:
$table->increments('id');
integer()
integer
方法創建一個 INTEGER
等效的欄位:
$table->integer('votes');
ipAddress()
ipAddress
方法創建一個 VARCHAR
等效的欄位:
$table->ipAddress('visitor');
在使用 PostgreSQL 時,將創建一個 INET
欄位。
json()
json
方法創建一個 JSON
等效的欄位:
$table->json('options');
在使用 SQLite 時,將創建一個 TEXT
欄位。
jsonb()
jsonb
方法創建一個 JSONB
等效的欄位:
$table->jsonb('options');
在使用 SQLite 時,將創建一個 TEXT
欄位。
longText()
longText
方法創建一個 LONGTEXT
等效的欄位:
$table->longText('description');
在使用 MySQL 或 MariaDB 時,您可以將 binary
字元集應用於欄位,以創建一個 LONGBLOB
等效的欄位:
$table->longText('data')->charset('binary'); // LONGBLOB
macAddress()
macAddress
方法創建一個用於保存 MAC 地址的欄位。某些資料庫系統(如 PostgreSQL)具有專用的欄位類型來保存此類型的資料。其他資料庫系統將使用等效的字串欄位:
$table->macAddress('device');
mediumIncrements()
mediumIncrements
方法創建一個自動遞增的 UNSIGNED MEDIUMINT
等效的主鍵欄位:
$table->mediumIncrements('id');
mediumInteger()
mediumInteger
方法創建一個 MEDIUMINT
等效的欄位:
$table->mediumInteger('votes');
mediumText()
mediumText
方法創建一個 MEDIUMTEXT
等效的欄位:
$table->mediumText('description');
在使用 MySQL 或 MariaDB 時,您可以將二進制字符集應用於欄位,以創建一個 MEDIUMBLOB
等效的欄位:
$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
欄位:
$table->morphs('taggable');
nullableMorphs()
此方法類似於 morphs 方法;但是,創建的欄位將是“可為空”:
$table->nullableMorphs('taggable');
nullableUlidMorphs()
此方法類似於 ulidMorphs 方法;但是,創建的欄位將是“可為空”:
$table->nullableUlidMorphs('taggable');
nullableUuidMorphs()
此方法類似於 uuidMorphs 方法;但是,創建的欄位將是“可為空”:
$table->nullableUuidMorphs('taggable');
rememberToken()
rememberToken
方法創建一個可為空的 VARCHAR(100)
等效的欄位,用於存儲當前的“記住我” 身份驗證標記:
```php
$table->rememberToken();
set()
set
方法使用給定的有效值列表創建 SET
等效的列:
$table->set('flavors', ['strawberry', 'vanilla']);
smallIncrements()
smallIncrements
方法創建一個自動增量的 UNSIGNED SMALLINT
等效列作為主鍵:
$table->smallIncrements('id');
smallInteger()
smallInteger
方法創建一個 SMALLINT
等效列:
$table->smallInteger('votes');
softDeletesTz()
softDeletesTz
方法添加一個可為空的帶有時區的 deleted_at
TIMESTAMP
等效列,可選擇性地具有小數秒精度。 這個列旨在存儲 Eloquent 的 "軟刪除" 功能所需的 deleted_at
時間戳:
$table->softDeletesTz('deleted_at', precision: 0);
softDeletes()
softDeletes
方法添加一個可為空的 deleted_at
TIMESTAMP
等效列,可選擇性地具有小數秒精度。 這個列旨在存儲 Eloquent 的 "軟刪除" 功能所需的 deleted_at
時間戳:
$table->softDeletes('deleted_at', precision: 0);
string()
string
方法創建給定長度的 VARCHAR
等效列:
$table->string('name', length: 100);
text()
text
方法創建一個 TEXT
等效列:
$table->text('description');
在使用 MySQL 或 MariaDB 時,您可以將 binary
字符集應用於列,以創建一個 BLOB
等效列:
$table->text('data')->charset('binary'); // BLOB
timeTz()
timeTz
方法創建具有可選小數秒精度的帶時區的 TIME
等效列:
$table->timeTz('sunrise', precision: 0);
time()
time
方法創建具有可選小數秒精度的 TIME
等效列:
$table->time('sunrise', precision: 0);
timestampTz()
timestampTz
方法創建具有可選小數秒精度的帶時區的 TIMESTAMP
等效列:
$table->timestampTz('added_at', precision: 0);
timestamp()
timestamp
方法創建具有可選小數秒精度的 TIMESTAMP
等效列:
$table->timestamp('added_at', precision: 0);
timestampsTz()
timestampsTz
方法創建具有可選小數秒精度的 created_at
和 updated_at
帶時區的 TIMESTAMP
等效列:
$table->timestampsTz(precision: 0);
timestamps()
timestamps
方法創建具有可選小數秒精度的 created_at
和 updated_at
的 TIMESTAMP
等效列:
$table->timestamps(precision: 0);
tinyIncrements()
tinyIncrements
方法創建自動增量的 UNSIGNED TINYINT
等效列作為主鍵:
$table->tinyIncrements('id');
tinyInteger()
tinyInteger
方法創建 TINYINT
等效列:
$table->tinyInteger('votes');
tinyText()
tinyText
方法創建一個 TINYTEXT
等效的欄位:
$table->tinyText('notes');
在使用 MySQL 或 MariaDB 時,您可以將 binary
字元集應用於欄位,以創建一個 TINYBLOB
等效的欄位:
$table->tinyText('data')->charset('binary'); // TINYBLOB
unsignedBigInteger()
unsignedBigInteger
方法創建一個 UNSIGNED BIGINT
等效的欄位:
$table->unsignedBigInteger('votes');
unsignedInteger()
unsignedInteger
方法創建一個 UNSIGNED INTEGER
等效的欄位:
$table->unsignedInteger('votes');
unsignedMediumInteger()
unsignedMediumInteger
方法創建一個 UNSIGNED MEDIUMINT
等效的欄位:
$table->unsignedMediumInteger('votes');
unsignedSmallInteger()
unsignedSmallInteger
方法創建一個 UNSIGNED SMALLINT
等效的欄位:
$table->unsignedSmallInteger('votes');
unsignedTinyInteger()
unsignedTinyInteger
方法創建一個 UNSIGNED TINYINT
等效的欄位:
$table->unsignedTinyInteger('votes');
ulidMorphs()
ulidMorphs
方法是一個方便的方法,它添加了一個 {column}_id
CHAR(26)
等效的欄位和一個 {column}_type
VARCHAR
等效的欄位。
此方法旨在在定義需要使用 ULID 識別符的多態 Eloquent 關係 所需的欄位時使用。在下面的示例中,將創建 taggable_id
和 taggable_type
欄位:
$table->ulidMorphs('taggable');
uuidMorphs()
uuidMorphs
方法是一個方便的方法,它會新增一個 {column}_id
CHAR(36)
等效的欄位和一個 {column}_type
VARCHAR
等效的欄位。
這個方法旨在在定義需要使用 UUID 識別符的多態 Eloquent 關聯 所需的欄位時使用。在下面的示例中,將創建 taggable_id
和 taggable_type
欄位:
$table->uuidMorphs('taggable');
ulid()
ulid
方法創建一個 ULID
等效的欄位:
$table->ulid('id');
uuid()
uuid
方法創建一個 UUID
等效的欄位:
$table->uuid('id');
vector()
vector
方法創建一個 vector
等效的欄位:
$table->vector('embedding', dimensions: 100);
year()
year
方法創建一個 YEAR
等效的欄位:
$table->year('birth_year');
欄位修飾符
除了上面列出的欄位類型外,在將欄位添加到資料庫表時,您可以使用幾個欄位“修飾符”。例如,要使欄位“可為空”,您可以使用 nullable
方法:
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
Schema::table('users', function (Blueprint $table) {
$table->string('email')->nullable();
});
以下表格包含所有可用的欄位修飾符。此列表不包括 索引修飾符: