數(shù)據(jù)庫遷移
數(shù)據(jù)庫遷移
數(shù)據(jù)庫:遷移
簡介
遷移就像是數(shù)據(jù)庫的版本控制,允許團隊簡單輕松的編輯并共享應(yīng)用的數(shù)據(jù)庫表結(jié)構(gòu),遷移通常和 Laravel 的 數(shù)據(jù)庫結(jié)構(gòu)生成器配合使用,讓你輕松地構(gòu)建數(shù)據(jù)庫結(jié)構(gòu)。如果你曾經(jīng)試過讓同事手動在數(shù)據(jù)庫結(jié)構(gòu)中添加字段,那么數(shù)據(jù)庫遷移可以讓你不再需要做這樣的事情。
Laravel 的 Schema
門面 對所有 Laravel 支持的數(shù)據(jù)庫系統(tǒng)提供了創(chuàng)建和操作數(shù)據(jù)表的相應(yīng)支持。
生成遷移
使用 Artisan 命令 make:migration
來創(chuàng)建遷移。
php artisan make:migration create_users_table
新的遷移位于 database/migrations
目錄下。每個遷移文件名都包含時間戳,以便讓 Laravel 確認遷移的順序。
--table
和 --create
選項可用來指定數(shù)據(jù)表的名稱,或是該遷移被執(zhí)行時是否將創(chuàng)建的新數(shù)據(jù)表。這些選項需在預(yù)生成遷移文件時填入指定的數(shù)據(jù)表:
php artisan make:migration create_users_table --create=users php artisan make:migration add_votes_to_users_table --table=users
如果你想要指定生成遷移指定一個自定義輸出路徑,則可以在運行 make:migration
命令時添加 --path
選項,給定的路徑必須是相對于應(yīng)用程序的基本路徑。
遷移結(jié)構(gòu)
遷移類通常會包含 2 個方法: up
和 down
。 up
方法用于添加新的數(shù)據(jù)表, 字段或者索引到數(shù)據(jù)庫, 而 down
方法就是 up
方法的反操作,和 up
里的操作相反。
在這 2 個方法中都要用到 Laravel 的 Schema
構(gòu)建器來創(chuàng)建和修改表,
若要了解 Schema
生成器中的所有可用方法 ,可以查看它的文檔。比如,創(chuàng)建 flights
表的簡單示例:
<?php use Illuminate\Support\Facades\Schema; use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class CreateFlightsTable extends Migration{ /** * 運行數(shù)據(jù)庫遷移 * * @return void */ public function up() { Schema::create('flights', function (Blueprint $table) { $table->increments('id'); $table->string('name'); $table->string('airline'); $table->timestamps(); }); } /** * 回滾數(shù)據(jù)庫遷移 * * @return void */ public function down() { Schema::drop('flights'); } }
運行遷移
執(zhí)行 Artisan 命令 migrate
來運行所有未完成的遷移:
php artisan migrate
{注意} 如果你正在使用 Homestead 虛擬機,你應(yīng)該在你的虛擬機里執(zhí)行這個命令。
在生產(chǎn)環(huán)境強制執(zhí)行遷移
一些遷移操作是具有破壞性的, 這意味著可能會導(dǎo)致數(shù)據(jù)丟失。 為了防止有人在生產(chǎn)環(huán)境中運行這些命令, 系統(tǒng)會在這些命令被運行之前與你進行確認。如果要強制忽略系統(tǒng)的提示運行命令, 則可以使用 --force
標記:
php artisan migrate --force
回滾遷移
若要回滾最后一次遷移, 可以使用 rollback
命令。 此命令將回滾最后一次 “遷移” 的操作,其中可能包含多個遷移文件:
php artisan migrate:rollback
你可以在 rollback
命令后面加上 step
參數(shù),來限制回滾遷移的個數(shù)。 例如,以下命令將回滾最近五次遷移:
php artisan migrate:rollback --step=5
migrate:reset
命令可以回滾應(yīng)用程序中的所有遷移:
php artisan migrate:reset
使用單個命令來執(zhí)行回滾或遷移
migrate:refresh
命令不僅會回滾數(shù)據(jù)庫的所有遷移還會接著運行 migrate
命令。 這個命令可以高效地重建整個數(shù)據(jù)庫:
php artisan migrate:refresh // 刷新數(shù)據(jù)庫結(jié)構(gòu)并執(zhí)行數(shù)據(jù)填充 php artisan migrate:refresh --seed
使用 refresh
命令并提供 step
參數(shù)來回滾并再執(zhí)行最后指定的遷移數(shù)。例如, 以下命令將回滾并重新執(zhí)行最后五次遷移:
php artisan migrate:refresh --step=5
刪除所有表 & 遷移
The migrate:fresh
命令會從數(shù)據(jù)庫中刪除所有表,然后執(zhí)行 migrate
命令:
php artisan migrate:fresh php artisan migrate:fresh --seed
數(shù)據(jù)表
創(chuàng)建數(shù)據(jù)表
可以使用 Schema
facade 的 create
方法來創(chuàng)建新的數(shù)據(jù)庫表。 create
方法接受兩個參數(shù):第一個參數(shù)為數(shù)據(jù)表的名稱,第二個參數(shù)是 Closure
,此閉包會接收一個用于定義新數(shù)據(jù)表的 Blueprint
對象:
Schema::create('users', function (Blueprint $table) { $table->increments('id'); });
當然,在創(chuàng)建數(shù)據(jù)表的時候,可以使用任何數(shù)據(jù)庫結(jié)構(gòu)生成器的 字段方法 來定義數(shù)據(jù)表的字段。
檢查數(shù)據(jù)表 / 字段是否存在
可以使用 hasTable
和 hasColumn
方法來檢查數(shù)據(jù)表或字段是否存在:
if (Schema::hasTable('users')) { // } if (Schema::hasColumn('users', 'email')) { // }
數(shù)據(jù)庫連接 & 表選項
如果要對非默認連接的數(shù)據(jù)庫連接執(zhí)行結(jié)構(gòu)操作,可以使用 connection
方法:
Schema::connection('foo')->create('users', function (Blueprint $table) { $table->increments('id'); });
你可以在數(shù)據(jù)庫結(jié)構(gòu)生成器上使用以下命令來定義表的選項:
命令 | 描述 |
---|---|
$table->engine = 'InnoDB'; | 指定表存儲引擎 (MySQL)。 |
$table->charset = 'utf8'; | 指定數(shù)據(jù)表的默認字符集 (MySQL)。 |
$table->collation = 'utf8_unicode_ci'; | 指定數(shù)據(jù)表默認的排序規(guī)則 (MySQL)。 |
$table->temporary(); | 創(chuàng)建臨時表 (不支持 SQL Server)。 |
重命名 / 刪除數(shù)據(jù)表
若要重命名數(shù)據(jù)表,可以使用 rename
方法:
Schema::rename($from, $to);
刪除一個已存在的數(shù)據(jù)表, 可使用 drop
或 dropIfExists
方法:
Schema::drop('users');Schema::dropIfExists('users');
重命名帶外鍵的數(shù)據(jù)表
在重命名表之前,你應(yīng)該驗證表上的任何外鍵約束在遷移文件中都有明確的名稱,而不是讓 Laravel 按照約定來設(shè)置一個名稱。否則,外鍵的約束名稱將引用舊表名。
字段
創(chuàng)建字段
使用 Schema
facade 的 table
方法可以更新現(xiàn)有的數(shù)據(jù)表。如同 create
方法一樣,table
方法會接受兩個參數(shù):一個是數(shù)據(jù)表的名稱,另一個則是接收可以用來向表中添加字段的 Blueprint
實例的閉包:
Schema::table('users', function (Blueprint $table) { $table->string('email'); });
可用的字段類型
數(shù)據(jù)庫結(jié)構(gòu)生成器包含構(gòu)建表時可以指定的各種字段類型:
命令 | 描述 |
---|---|
$table->bigIncrements('id'); | 遞增 ID(主鍵),相當于「UNSIGNED BIG INTEGER」 |
$table->bigInteger('votes'); | 相當于 BIGINT |
$table->binary('data'); | 相當于 BLOB |
$table->boolean('confirmed'); | 相當于 BOOLEAN |
$table->char('name', 100); | 相當于帶有長度的 CHAR |
$table->date('created_at'); | 相當于 DATE |
$table->dateTime('created_at'); | 相當于 DATETIME |
$table->dateTimeTz('created_at'); | 相當于帶時區(qū) DATETIME |
$table->decimal('amount', 8, 2); | 相當于帶有精度與基數(shù) DECIMAL |
$table->double('amount', 8, 2); | 相當于帶有精度與基數(shù) DOUBLE |
$table->enum('level', ['easy', 'hard']); | 相當于 ENUM |
$table->float('amount', 8, 2); | 相當于帶有精度與基數(shù) FLOAT |
$table->geometry('positions'); | 相當于 GEOMETRY |
$table->geometryCollection('positions'); | 相當于 GEOMETRYCOLLECTION |
$table->increments('id'); | 遞增的 ID (主鍵),相當于「UNSIGNED INTEGER」 |
$table->integer('votes'); | 相當于 INTEGER |
$table->ipAddress('visitor'); | 相當于 IP 地址 |
$table->json('options'); | 相當于 JSON |
$table->jsonb('options'); | 相當于 JSONB |
$table->lineString('positions'); | 相當于 LINESTRING |
$table->longText('description'); | 相當于 LONGTEXT |
$table->macAddress('device'); | 相當于 MAC 地址 |
$table->mediumIncrements('id'); | 遞增 ID (主鍵) ,相當于「UNSIGNED MEDIUM INTEGER」 |
$table->mediumInteger('votes'); | 相當于 MEDIUMINT |
$table->mediumText('description'); | 相當于 MEDIUMTEXT |
$table->morphs('taggable'); | 相當于加入遞增的 taggable_id 與字符串 taggable_type |
$table->multiLineString('positions'); | 相當于 MULTILINESTRING |
$table->multiPoint('positions'); | 相當于 MULTIPOINT |
$table->multiPolygon('positions'); | 相當于 MULTIPOLYGON |
$table->nullableMorphs('taggable'); | 相當于可空版本的 morphs() 字段 |
$table->nullableTimestamps(); | 相當于可空版本的 timestamps() 字段 |
$table->point('position'); | 相當于 POINT |
$table->polygon('positions'); | 相當于 POLYGON |
$table->rememberToken(); | 相當于可空版本的 VARCHAR (100) 的 remember_token 字段 |
$table->smallIncrements('id'); | 遞增 ID (主鍵) ,相當于「UNSIGNED SMALL INTEGER」 |
$table->smallInteger('votes'); | 相當于 SMALLINT |
$table->softDeletes(); | 相當于為軟刪除添加一個可空的 deleted_at 字段 |
$table->softDeletesTz(); | 相當于為軟刪除添加一個可空的 帶時區(qū)的 deleted_at 字段 |
$table->string('name', 100); | 相當于帶長度的 VARCHAR |
$table->text('description'); | 相當于 TEXT |
$table->time('sunrise'); | 相當于 TIME |
$table->timeTz('sunrise'); | 相當于帶時區(qū)的 TIME |
$table->timestamp('added_on'); | 相當于 TIMESTAMP |
$table->timestampTz('added_on'); | 相當于帶時區(qū)的 TIMESTAMP |
$table->timestamps(); | 相當于可空的 created_at 和 updated_at TIMESTAMP |
$table->timestampsTz(); | 相當于可空且?guī)r區(qū)的 created_at 和 updated_at TIMESTAMP |
$table->tinyIncrements('id'); | 相當于自動遞增 UNSIGNED TINYINT |
$table->tinyInteger('votes'); | 相當于 TINYINT |
$table->unsignedBigInteger('votes'); | 相當于 Unsigned BIGINT |
$table->unsignedDecimal('amount', 8, 2); | 相當于帶有精度和基數(shù)的 UNSIGNED DECIMAL |
$table->unsignedInteger('votes'); | 相當于 Unsigned INT |
$table->unsignedMediumInteger('votes'); | 相當于 Unsigned MEDIUMINT |
$table->unsignedSmallInteger('votes'); | 相當于 Unsigned SMALLINT |
$table->unsignedTinyInteger('votes'); | 相當于 Unsigned TINYINT |
$table->uuid('id'); | 相當于 UUID |
$table->year('birth_year'); | 相當于 YEAR |
字段修飾
除了上述列出的字段類型之外,還有幾個可以在添加字段到數(shù)據(jù)庫表時使用的 “修飾符”。例如,如果要把字段設(shè)置為 “可空 ", 你可以使用 nullable
方法:
Schema::table('users', function (Blueprint $table) { $table->string('email')->nullable(); });
以下是所有可用的字段修飾符的列表。此列表不包括 索引修飾符:
Modifier | Description |
---|---|
->after('column') | 將此字段放置在其它字段 "之后" (MySQL) |
->autoIncrement() | 將 INTEGER 類型的字段設(shè)置為自動遞增的主鍵 |
->charset('utf8') | 指定一個字符集 (MySQL) |
->collation('utf8_unicode_ci') | 指定列的排序規(guī)則 (MySQL/SQL Server) |
->comment('my comment') | 為字段增加注釋 (MySQL) |
->default($value) | 為字段指定 "默認" 值 |
->first() | 將此字段放置在數(shù)據(jù)表的 "首位" (MySQL) |
->nullable($value = true) | 此字段允許寫入 NULL 值(默認情況下) |
->storedAs($expression) | 創(chuàng)建一個存儲生成的字段 (MySQL) |
->unsigned() | 設(shè)置 INTEGER 類型的字段為 UNSIGNED (MySQL) |
->useCurrent() | 將 TIMESTAMP 類型的字段設(shè)置為使用 CURRENT_TIMESTAMP 作為默認值 |
->virtualAs($expression) | 創(chuàng)建一個虛擬生成的字段 (MySQL) |
修改字段
先決條件
在修改字段之前,請確保將 doctrine/dbal
依賴添加到 composer.json
文件中。Doctrine DBAL 庫用于確定字段的當前狀態(tài), 并創(chuàng)建對該字段進行指定調(diào)整所需的 SQL 查詢:
composer require doctrine/dbal
更新字段屬性
change
方法可以將現(xiàn)有的字段類型修改為新的類型或修改屬性。
比如,你可能想增加。字符串字段的長度,可以使用 change
方法把 name
字段的長度從 25 增加到 50:
Schema::table('users', function (Blueprint $table) { $table->string('name', 50)->change(); });
我們應(yīng)該將字段修改為可空:
Schema::table('users', function (Blueprint $table) { $table->string('name', 50)->nullable()->change(); });
{注} 只有下面的字段類型能被 "修改": bigInteger、 binary、 boolean、date、dateTime、dateTimeTz、decimal、integer、json、 longText、mediumText、smallInteger、string、text、time、 unsignedBigInteger、unsignedInteger and unsignedSmallInteger。
重命名字段
可以使用結(jié)構(gòu)生成器上的 renameColumn
方法來重命名字段。在重命名字段前 , 請確保你的 composer.json
文件內(nèi)已經(jīng)加入 doctrine/dbal
依賴:
Schema::table('users', function (Blueprint $table) { $table->renameColumn('from', 'to'); });
{注} 當前不支持 enum 類型的字段重命名。
刪除字段
可以使用結(jié)構(gòu)生成器上的 dropColumn
方法來刪除字段。 在從 SQLite 數(shù)據(jù)庫刪除字段前,你需要在 composer.json
文件中加入 doctrine/dbal
依賴并在終端執(zhí)行 composer update
來安裝該依賴:
Schema::table('users', function (Blueprint $table) { $table->dropColumn('votes'); });
你可以傳遞一個字段數(shù)組給 dropColumn
方法來刪除多個字段:
Schema::table('users', function (Blueprint $table) { $table->dropColumn(['votes', 'avatar', 'location']); });
{注} 不支持在使用 SQLite 數(shù)據(jù)庫時在單個遷移中刪除或修改多個字段。
可用的命令別名
Command | Description |
---|---|
$table->dropRememberToken(); | 刪除 remember_token 字段。 |
$table->dropSoftDeletes(); | 刪除 deleted_at 字段。 |
$table->dropSoftDeletesTz(); | dropSoftDeletes() 方法的別名。 |
$table->dropTimestamps(); | 刪除 created_at and updated_at 字段。 |
$table->dropTimestampsTz(); | dropTimestamps() 方法的別名。 |
索引
創(chuàng)建索引
結(jié)構(gòu)生成器支持多種類型的索引。首先,先指定字段值唯一,即簡單地在字段定義 之后鏈式調(diào)用 unique
方法來創(chuàng)建索引,例如:
$table->string('email')->unique();
或者,你也可以在定義完字段之后創(chuàng)建索引。例如:
$table->unique('email');
你甚至可以將數(shù)組傳遞給索引方法來創(chuàng)建一個復(fù)合(或合成)索引:
$table->index(['account_id', 'created_at']);
Laravel 會自動生成一個合理的索引名稱,但你也可以傳遞第二個參數(shù)來自定義索引名稱:
$table->unique('email', 'unique_email');
可用的索引類型
每個索引方法都接受一個可選的第二個參數(shù)來指定索引的名稱。如果省略,名稱將根據(jù)表和列的名稱生成。
命令 | 描述 |
---|---|
$table->primary('id'); | 添加主鍵 |
$table->primary(['id', 'parent_id']); | 添加復(fù)合鍵 |
$table->unique('email'); | 添加唯一索引 |
$table->index('state'); | 添加普通索引 |
$table->spatialIndex('location'); | 添加空間索引(不支持 SQLite) |
索引長度 & Mysql / MariaDB
Laravel 默認使用 utf8mb4
編碼,它支持在數(shù)據(jù)庫中儲存 emojis
。如果你是在版本低于 5.7.7 的 MySQL 或者版本低于 10.2.2 的 MariaDB 上創(chuàng)建索引,那你就需要手動配置數(shù)據(jù)庫遷移的默認字符串長度。
即在 AppServiceProvider
中調(diào)用 Schema::defaultStringLength
方法來配置它:
use Illuminate\Support\Facades\Schema; /** * 引導(dǎo)任何應(yīng)用程序服務(wù) * * @return void */ public function boot() { Schema::defaultStringLength(191); }
當然,你也可以選擇開啟數(shù)據(jù)庫的 innodb_large_prefix
選項。至于如何正確開啟,請自行查閱數(shù)據(jù)庫文檔。
重命名索引
若要重命名索引,你需要調(diào)用 renameIndex
方法。此方法接受當前索引名稱作為其第一個參數(shù),并將所需名稱作為其第二個參數(shù):
你需要將當前索引名稱作為其第一個參數(shù),并將新索引名稱作為其第二個參數(shù):
$table->renameIndex('from', 'to')
刪除索引
若要刪除索引,則必須指定索引的名稱。Laravel 默認會自動將數(shù)據(jù)庫名稱、索引的字段名及索引類型簡單地連接在一起作為名稱。舉例如下:
命令 | 描述 |
---|---|
$table->dropPrimary('users_id_primary'); | 從 users 表中刪除主鍵 |
$table->dropUnique('users_email_unique'); | 從 users 表中刪除唯一索引 |
$table->dropIndex('geo_state_index'); | 從 geo 表中刪除基本索引 |
$table->dropSpatialIndex('geo_location_spatialindex'); | 從 geo 表中刪除空間索引(不支持 SQLite) |
如果將字段數(shù)組傳給 dropIndex
方法,會刪除根據(jù)表名、字段和鍵類型生成的索引名稱。
Schema::table('geo', function (Blueprint $table) { $table->dropIndex(['state']); // 刪除 'geo_state_index' 索引 });
外鍵約束
Laravel 還支持創(chuàng)建用于在數(shù)據(jù)庫層中的強制引用完整性的外鍵約束。例如,讓我們在 posts
表上定義一個引用 users
表的 id
字段的 user_id
字段:
Schema::table('posts', function (Blueprint $table) { $table->unsignedInteger('user_id'); $table->foreign('user_id')->references('id')->on('users'); });
還可以為 on delete
和 on update
屬性指定所需的操作:
$table->foreign('user_id') ->references('id')->on('users') ->onDelete('cascade');
你可以使用 dropForeign
方法來刪除外鍵。外鍵約束采用的命名方式與索引相同。即,將數(shù)據(jù)表名稱和約束的字段連接起來,再加上 _foreign
后綴:
$table->dropForeign('posts_user_id_foreign');
或者,你也可以傳遞一個字段數(shù)組,在刪除的時候會按照約定字段轉(zhuǎn)換為對應(yīng)的外鍵名稱:
$table->dropForeign(['user_id']);
你可以在遷移文件中使用以下方法來開啟或關(guān)閉外鍵約束:
Schema::enableForeignKeyConstraints(); Schema::disableForeignKeyConstraints();
{注} SQLite 默認禁用外鍵約束。 使用 SQLite 時,請確保在數(shù)據(jù)庫配置中啟用 [啟用外鍵支持](/docs/laravel/5.8 /database#configuration),然后再嘗試在遷移中創(chuàng)建它們。