* * * * *
[TOC]
## 簡介
數據庫遷移就像是數據庫的版本控制,可以讓你的團隊輕松修改并共享應用程序的數據庫結構。遷移通常會搭配上 Laravel 的數據庫結構構造器來讓你方便地構建數據庫結構。如果你曾經出現過讓同事手動在數據庫結構中添加字段的情況,數據庫遷移可以解決你這個問題。
Laravel 的?`Schema`?[facade](https://laravel-china.org/docs/laravel/5.4/facades)?對所有 Laravel 支持的數據庫系統提供了創建和操作數據表的相應支持。
## 生成遷移
使用?`make:migration`?[Artisan 命令](https://laravel-china.org/docs/laravel/5.4/artisan)?來創建遷移:
~~~
php artisan make:migration create_users_table
~~~
新的遷移文件將會被放置在?`database/migrations`?目錄中。每個遷移文件的名稱都包含了一個時間戳,以便讓 Laravel 確認遷移的順序。
`--table`?和?`--create`?選項可用來指定數據表的名稱,或是該遷移被執行時會創建的新數據表。這些選項需在預生成遷移文件時填入指定的數據表:
~~~
php artisan make:migration create_users_table --create=users
php artisan make:migration add_votes_to_users_table --table=users
~~~
如果你想為生成的遷移指定一個自定義輸出路徑,則可以在運行?`make:migration`?命令時添加?`--path`?選項。提供的路徑必須是相對于應用程序的基本路徑。
## 遷移結構
一個遷移類會包含兩個方法:?`up`?和?`down`?。?`up`?方法可為數據庫添加新的數據表、字段或索引,而?`down`?方法則是?`up`?方法的逆操作。
你可以在這兩個方法中使用 Laravel 數據庫結構構造器來創建以及修改數據表。若要了解?`數據庫結構`?構造器中的所有可用方法,[可查閱它的文檔](https://laravel-china.org/docs/laravel/5.4/migrations/1261#creating-tables)。以下的遷移實例會創建一張?`flights`?數據表:
~~~
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateFlightsTable extends Migration
{
/**
* 運行數據庫遷移
*
* @return void
*/
public function up()
{
Schema::create('flights', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->string('airline');
$table->timestamps();
});
}
/**
* 回滾數據庫遷移
*
* @return void
*/
public function down()
{
Schema::drop('flights');
}
}
~~~
## 運行遷移
使用?`migrate`?Artisan 命令,來運行所有未運行過的遷移:
~~~
php artisan migrate
~~~
> {note} 如果你使用的是?[Homestead 虛擬機](https://laravel-china.org/docs/laravel/5.4/homestead)?, 你需要在虛擬機中執行以上命令。
#### 在線上環境強制執行遷移
一些遷移的操作是具有破壞性的,它們可能會導致數據丟失。為了保護線上環境的數據庫,系統會在這些命令被運行之前顯示確認提示。若要忽略此提示并強制運行命令,則可以使用?`--force`?標記:
~~~
php artisan migrate --force
~~~
### 回滾遷移
若要回滾最后一次遷移,則可以使用?`rollback`?命令。此命令是對上一次執行的「批量」遷移回滾,其中可能包括多個遷移文件:
~~~
php artisan migrate:rollback
~~~
在?`rollback`?命令后加上?`step`?參數,你可以限制回滾遷移的個數。例如,下面的命令將會回滾最后的 5 個遷移。
~~~
php artisan migrate:rollback --step=5
~~~
`migrate:reset`?命令可以回滾應用程序中的所有遷移:
~~~
php artisan migrate:reset
~~~
#### 使用單個命令來執行回滾或遷移
`migrate:refresh`?命令不僅會回滾數據庫的所有遷移還會接著運行?`migrate`?命令。所以此命令可以有效的重新創建整個數據庫:
~~~
php artisan migrate:refresh
// 刷新數據庫結構并執行數據填充
php artisan migrate:refresh --seed
~~~
使用?`refresh`?命令并加上?`step`?參數,你也可以限制執行回滾和再遷移的個數。比如,下面的命令會回滾并再遷移最后的 5 個遷移:
~~~
php artisan migrate:refresh --step=5
~~~
## 數據表
### 創建數據表
要創建一張新的數據表,可以使用?`Schema`?facade 的?`create`?方法。`create`?方法接收兩個參數:第一個參數為數據表的名稱,第二個參數為一個?`閉包`?,此閉包會接收一個用于定義新數據表的?`Blueprint`?對象:
~~~
Schema::create('users', function (Blueprint $table) {
$table->increments('id');
});
~~~
當然,在創建數據表的時候,你也可以使用任何數據庫結構構造器的?[字段方法](https://laravel-china.org/docs/laravel/5.4/migrations/1261#creating-columns)?來定義數據表的字段。
#### 檢查數據表或字段是否存在
你可以方便地使用?`hasTable`?和?`hasColumn`?方法來檢查數據表或字段是否存在:
~~~
if (Schema::hasTable('users')) {
//
}
if (Schema::hasColumn('users', 'email')) {
//
}
~~~
#### 數據庫連接與存儲引擎
如果你想要在一個非默認的數據庫連接中進行數據庫結構操作,可以使用?`connection`?方法:
~~~
Schema::connection('foo')->create('users', function (Blueprint $table) {
$table->increments('id');
});
~~~
你可以在數據庫結構構造器上設置?`engine`?屬性來設置數據表的存儲引擎:
~~~
Schema::create('users', function (Blueprint $table) {
$table->engine = 'InnoDB';
$table->increments('id');
});
~~~
### 重命名與刪除數據表
若要重命名一張已存在的數據表,可以使用?`rename`?方法:
~~~
Schema::rename($from, $to);
~~~
要刪除已存在的數據表,可使用?`drop`?或?`dropIfExists`?方法:
~~~
Schema::drop('users');
Schema::dropIfExists('users');
~~~
#### 重命名帶外鍵的數據表
在重命名前,你需要檢查外鍵的約束涉及到的數據表名需要在遷移文件中顯式的提供,而不是讓 Laravel 按照約定來設置一個名稱。因為那樣會讓外鍵約束關聯到舊的數據表上。
## 字段
### 創建字段
使用?`Schema`?facade 的?`table`?方法可以更新已有的數據表。如同?`create`?方法,`table`?方法會接收兩個參數:一個是數據表的名稱,另一個則是接收?`Blueprint`?實例的`閉包`。我們可以使用它來為數據表新增字段:
~~~
Schema::table('users', function (Blueprint $table) {
$table->string('email');
});
~~~
#### 可用的字段類型
數據庫結構構造器包含了許多字段類型,供你構建數據表時使用:
| 命令 | 描述 |
| --- | --- |
| `$table->bigIncrements('id');` | 遞增 ID(主鍵),相當于「UNSIGNED BIG INTEGER」型態。 |
| `$table->bigInteger('votes');` | 相當于 BIGINT 型態。 |
| `$table->binary('data');` | 相當于 BLOB 型態。 |
| `$table->boolean('confirmed');` | 相當于 BOOLEAN 型態。 |
| `$table->char('name', 4);` | 相當于 CHAR 型態,并帶有長度。 |
| `$table->date('created_at');` | 相當于 DATE 型態 |
| `$table->dateTime('created_at');` | 相當于 DATETIME 型態。 |
| `$table->dateTimeTz('created_at');` | DATETIME (帶時區) 形態 |
| `$table->decimal('amount', 5, 2);` | 相當于 DECIMAL 型態,并帶有精度與基數。 |
| `$table->double('column', 15, 8);` | 相當于 DOUBLE 型態,總共有 15 位數,在小數點后面有 8 位數。 |
| `$table->enum('choices', ['foo', 'bar']);` | 相當于 ENUM 型態。 |
| `$table->float('amount', 8, 2);` | 相當于 FLOAT 型態,總共有 8 位數,在小數點后面有 2 位數。 |
| `$table->increments('id');` | 遞增的 ID (主鍵),使用相當于「UNSIGNED INTEGER」的型態。 |
| `$table->integer('votes');` | 相當于 INTEGER 型態。 |
| `$table->ipAddress('visitor');` | 相當于 IP 地址形態。 |
| `$table->json('options');` | 相當于 JSON 型態。 |
| `$table->jsonb('options');` | 相當于 JSONB 型態。 |
| `$table->longText('description');` | 相當于 LONGTEXT 型態。 |
| `$table->macAddress('device');` | 相當于 MAC 地址形態。 |
| `$table->mediumIncrements('id');` | 遞增 ID (主鍵) ,相當于「UNSIGNED MEDIUM INTEGER」型態。 |
| `$table->mediumInteger('numbers');` | 相當于 MEDIUMINT 型態。 |
| `$table->mediumText('description');` | 相當于 MEDIUMTEXT 型態。 |
| `$table->morphs('taggable');` | 加入整數?`taggable_id`?與字符串?`taggable_type`。 |
| `$table->nullableMorphs('taggable');` | 與?`morphs()`?字段相同,但允許為NULL。 |
| `$table->nullableTimestamps();` | 與?`timestamps()`?相同,但允許為 NULL。 |
| `$table->rememberToken();` | 加入?`remember_token`?并使用 VARCHAR(100) NULL。 |
| `$table->smallIncrements('id');` | 遞增 ID (主鍵) ,相當于「UNSIGNED SMALL INTEGER」型態。 |
| `$table->smallInteger('votes');` | 相當于 SMALLINT 型態。 |
| `$table->softDeletes();` | 加入?`deleted_at`?字段用于軟刪除操作。 |
| `$table->string('email');` | 相當于 VARCHAR 型態。 |
| `$table->string('name', 100);` | 相當于 VARCHAR 型態,并帶有長度。 |
| `$table->text('description');` | 相當于 TEXT 型態。 |
| `$table->time('sunrise');` | 相當于 TIME 型態。 |
| `$table->timeTz('sunrise');` | 相當于 TIME (帶時區) 形態。 |
| `$table->tinyInteger('numbers');` | 相當于 TINYINT 型態。 |
| `$table->timestamp('added_on');` | 相當于 TIMESTAMP 型態。 |
| `$table->timestampTz('added_on');` | 相當于 TIMESTAMP (帶時區) 形態。 |
| `$table->timestamps();` | 加入?`created_at`?和?`updated_at`?字段。 |
| `$table->timestampsTz();` | 加入?`created_at`?and?`updated_at`?(帶時區) 字段,并允許為NULL。 |
| `$table->unsignedBigInteger('votes');` | 相當于 Unsigned BIGINT 型態。 |
| `$table->unsignedInteger('votes');` | 相當于 Unsigned INT 型態。 |
| `$table->unsignedMediumInteger('votes');` | 相當于 Unsigned MEDIUMINT 型態。 |
| `$table->unsignedSmallInteger('votes');` | 相當于 Unsigned SMALLINT 型態。 |
| `$table->unsignedTinyInteger('votes');` | 相當于 Unsigned TINYINT 型態。 |
| `$table->uuid('id');` | 相當于 UUID 型態。 |
### 字段修飾
除了上述的字段類型列表,還有一些其它的字段「修飾」,你可以將它增加到字段中。例如,若要讓字段「nullable」,那么你可以使用?`nullable`?方法:
~~~
Schema::table('users', function (Blueprint $table) {
$table->string('email')->nullable();
});
~~~
以下列表為字段的可用修飾。此列表不包括?[索引修飾](https://laravel-china.org/docs/laravel/5.4/migrations/1261#creating-indexes):
| Modifier | Description |
| --- | --- |
| `->after('column')` | 將此字段放置在其它字段「之后」(僅限 MySQL) |
| `->comment('my comment')` | 增加注釋 |
| `->default($value)` | 為此字段指定「默認」值 |
| `->first()` | 將此字段放置在數據表的「首位」(僅限 MySQL) |
| `->nullable()` | 此字段允許寫入 NULL 值 |
| `->storedAs($expression)` | 創建一個存儲的生成字段 (僅限 MySQL) |
| `->unsigned()` | 設置?`integer`?字段為?`UNSIGNED` |
| `->virtualAs($expression)` | 創建一個虛擬的生成字段 (僅限 MySQL) |
### 修改字段
#### 先決條件
在修改字段之前,請務必在你的?`composer.json`?中增加?`doctrine/dbal`?依賴。Doctrine DBAL 函數庫被用于判斷當前字段的狀態以及創建調整指定字段的 SQL 查詢。
~~~
composer require doctrine/dbal
~~~
#### 更新字段屬性
`change`?方法讓你可以修改一些已存在的字段類型,或修改字段屬性。比如,你可能想增加字符串字段的長度。想了解?`change`?方法如何使用,讓我們來把?`name`?字段的長度從 25 增加到 50 :
~~~
Schema::table('users', function (Blueprint $table) {
$table->string('name', 50)->change();
});
~~~
我們也能將字段修改為 nullable:
~~~
Schema::table('users', function (Blueprint $table) {
$table->string('name', 50)->nullable()->change();
});
~~~
> {note} 下面的字段類型不能被「修改」: char,double,enum,mediumInteger,timestamp,tinyInteger,ipAddress,json,jsonb,macAddress,mediumIncrements,morphs,nullableMorphs,nullableTimestamps,softDeletes,timeTz,timestampTz,timestamps,timestampsTz,unsignedMediumInteger,unsignedTinyInteger,uuid。
#### 重命名字段
要重命名字段,可使用數據庫結構構造器的?`renameColumn`?方法。在重命名字段前,請確保你的?`composer.json`?文件內已經加入?`doctrine/dbal`?依賴:
~~~
Schema::table('users', function (Blueprint $table) {
$table->renameColumn('from', 'to');
});
~~~
> {note} 數據表的?`enum`?字段暫時不支持修改字段屬性。
### 移除字段
要移除字段,可使用數據庫結構構造器的?`dropColumn`?方法。在刪除 SQLite 數據庫的字段前,你需要在?`composer.json`?文件中加入?`doctrine/dbal`?依賴并在終端執行?`composer update`?來安裝函數庫:
~~~
Schema::table('users', function (Blueprint $table) {
$table->dropColumn('votes');
});
~~~
你可以傳遞多個字段的數組至?`dropCloumn`?方法來移除多個字段:
~~~
Schema::table('users', function (Blueprint $table) {
$table->dropColumn(['votes', 'avatar', 'location']);
});
~~~
> {note} SQLite 數據庫并不支持在單個遷移中移除或修改多個字段。
## 索引
### 創建索引
數據庫結構構造器支持多種類型的索引。首先,讓我們先來看一個示例,其指定了字段的值必須是唯一的。你可以簡單的在字段定義之后鏈式調用?`unique`?方法來創建索引:
~~~
$table->string('email')->unique();
~~~
此外,你也可以在定義完字段之后創建索引。例如:
~~~
$table->unique('email');
~~~
你也可以傳遞一個字段的數組至索引方法來創建復合索引:
~~~
$table->index(['account_id', 'created_at']);
~~~
Laravel 會自動生成一個合理的索引名稱,但你也可以使用第二個參數來自定義索引名稱:
~~~
$table->index('email', 'my_index_name');
~~~
#### 可用的索引類型
| Command | Description |
| --- | --- |
| `$table->primary('id');` | 加入主鍵。 |
| `$table->primary(['first', 'last']);` | 加入復合鍵。 |
| `$table->unique('email');` | 加入唯一索引。 |
| `$table->unique('state', 'my_index_name');` | 自定義索引名稱。 |
| `$table->unique(['first', 'last']);` | 加入復合唯一鍵。 |
| `$table->index('state');` | 加入基本索引。 |
#### 索引長度 & MySQL / MariaDB
Laravel 默認使用?`utf8mb4`?字符,包括支持在數據庫存儲「表情」。如果你正在運行的 MySQL release 版本低于5.7.7 或 MariaDB release 版本低于10.2.2 ,為了MySQL為它們創建索引,你可能需要手動配置遷移生成的默認字符串長度,你可以通過調用?`AppServiceProvider`?中的?`Schema::defaultStringLength`?方法來配置它:
~~~
use Illuminate\Support\Facades\Schema;
/**
* 引導任何應用程序服務。
*
* @return void
*/
public function boot()
{
Schema::defaultStringLength(191);
}
~~~
或者你可以為數據庫開啟?`innodb_large_prefix`?選項,有關如何正確開啟此選項的說明請查閱數據庫文檔。
### 移除索引
若要移除索引,則必須指定索引的名稱。Laravel 默認會自動給索引分配合理的名稱。其將數據表名稱、索引的字段名稱及索引類型簡單地連接在了一起。舉例如下:
| 命令 | 描述 |
| --- | --- |
| `$table->dropPrimary('users_id_primary');` | 從「users」數據表移除主鍵。 |
| `$table->dropUnique('users_email_unique');` | 從「users」數據表移除唯一索引。 |
| `$table->dropIndex('geo_state_index');` | 從「geo」數據表移除基本索引。 |
如果你對?`dropIndex`?傳參索引數組,默認的約定是索引名稱由數據庫表名字和鍵名拼接而成:
~~~
Schema::table('geo', function (Blueprint $table) {
$table->dropIndex(['state']); // 移除索引 'geo_state_index'
});
~~~
### 外鍵約束
Laravel 也為創建外鍵約束提供了支持,用于在數據庫層中的強制引用完整性。例如,讓我們定義一個有?`user_id`?字段的?`posts`?數據表,`user_id`?引用了?`users`?數據表的?`id`?字段:
~~~
Schema::table('posts', function (Blueprint $table) {
$table->integer('user_id')->unsigned();
$table->foreign('user_id')->references('id')->on('users');
});
~~~
你也可以指定約束的「on delete」及「on update」:
~~~
$table->foreign('user_id')
->references('id')->on('users')
->onDelete('cascade');
~~~
要移除外鍵,你可以使用?`dropForeign`?方法。外鍵約束與索引采用相同的命名方式。所以,我們可以將數據表名稱和約束字段連接起來,接著在該名稱后面加上「_foreign」后綴:
~~~
$table->dropForeign('posts_user_id_foreign');
~~~
你也可以傳遞一個包含字段的數組,在移除的時候字段會按照慣例被自動轉換為對應的外鍵名稱:
~~~
$table->dropForeign(['user_id']);
~~~
你可以在遷移文件里使用以下方法來開啟和關閉外鍵約束:
~~~
Schema::enableForeignKeyConstraints();
Schema::disableForeignKeyConstraints();
~~~
- 前言
- 翻譯說明
- 發行說明
- 升級說明
- 貢獻導引
- 入門指南
- 安裝
- 配置信息
- 文件夾結構
- 請求周期
- 開發環境部署
- Homestead
- Valet
- 核心概念
- 服務容器
- 服務提供者
- Facades
- Contracts
- HTTP層
- 路由
- 中間件
- CSRF 保護
- 控制器
- 請求
- 響應
- 視圖
- Session
- 表單驗證
- 前端
- Blade 模板
- 本地化
- 前端指南
- 編輯資源 Mix
- 安全
- 用戶認證
- Passport OAuth 認證
- 用戶授權
- 加密解密
- 哈希
- 重置密碼
- 綜合話題
- Artisan 命令行
- 廣播系統
- 緩存系統
- 集合
- 錯誤與日志
- 事件系統
- 文件存儲
- 輔助函數
- 郵件發送
- 消息通知
- 擴展包開發
- 隊列
- 任務調度
- 數據庫
- 快速入門
- 查詢構造器
- 分頁
- 數據庫遷移
- 數據填充
- Redis
- Eloquent ORM
- 快速入門
- 模型關聯
- Eloquent 集合
- 修改器
- 序列化
- 測試
- 快速入門
- HTTP 測試
- 瀏覽器測試 Dusk
- 數據庫測試
- 測試模擬器
- 官方擴展包
- Cashier 交易工具包
- Envoy 部署工具
- Scout 全文搜索
- Socialite 社會化登錄