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