* * * * *
[TOC]
## 簡介
遷移就像是數據庫的版本控制, 允許團隊簡單輕松的編輯并共享應用的數據庫表結構,遷移通常和 Laravel 的 數據庫結構生成器配合使用,讓你輕松地構建數據庫結構。如果你曾經試過讓同事手動在數據庫結構中添加字段,那么數據庫遷移可以讓你不再需要做這樣的事情。
Laravel 的?`Schema`?[門面](http://www.hmoore.net/tonyyu/laravel_5_6/786058)?對所有 Laravel 支持的數據庫系統提供了創建和操作數據表的相應支持。
## 生成遷移
使用?[Artisan命令](http://www.hmoore.net/tonyyu/laravel_5_6/786238)?`make:migration`?來創建遷移。
~~~
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`?選項,給定的路徑必須是相對于應用程序的基本路徑。
## 遷移結構
遷移類通常會包含2個方法:?`up`?和?`down`。?`up`?方法用于添加新的數據表, 字段或者索引到數據庫, 而?`down`?方法就是?`up`?方法的反操作,和?`up`?里的操作相反。
在這2個方法中都要用到Laravel 的?`Schema`?構建器來創建和修改表,
若要了解?`Schema`?生成器中的所有可用方法 ,[可以查看它的文檔](http://www.hmoore.net/tonyyu/laravel_5_6/786263#_138)。比如,創建?`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');
}
}
~~~
## 運行遷移
使用 Artisan命令?`migrate`?方法來運行所有未完成的遷移:
~~~
php artisan migrate
~~~
> {注} 如果正在使用?[Homestead虛擬機](http://www.hmoore.net/tonyyu/laravel_5_6/786195), 你應該在你的虛擬機下運行這個命令。
#### 在生產環境強制執行遷移
一些遷移操作是具有破壞性的, 這意味著可能會導致數據丟失。 為了防止有人在生產環境中運行這些命令, 系統會在這些命令被運行之前與你進行確認。如果要強制忽略系統的提示運行命令, 則可以使用?`--force`?標記:
~~~
php artisan migrate --force
~~~
### 回滾遷移
若要回滾最后一次遷移, 可以使用?`rollback`?命令。 此命令將回滾最后一次“遷移”的操作,其中可能包含多個遷移文件:
~~~
php artisan migrate:rollback
~~~
你可以在?`rollback`?命令后面加上?`step`?參數,來限制回滾遷移的個數。 例如,以下命令將回滾最近五次遷移:
~~~
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`?參數來回滾并再執行最后指定的遷移數。例如, 以下命令將回滾并重新執行最后五次遷移:
~~~
php artisan migrate:refresh --step=5
~~~
#### 刪除所有表 & 遷移
`migrate:fresh`?命令會從數據庫中刪除所有表,然后執行?`migrate`?命令:
~~~
php artisan migrate:fresh
php artisan migrate:fresh --seed
~~~
## 數據表
### 創建數據表
可以使用?`Schema`?facade的?`create`?方法來創建新的數據庫表。?`create`?方法接受兩個參數:第一個參數為數據表的名稱,第二個參數是?`Closure`?,此閉包會接收一個用于定義新數據表的?`Blueprint`?對象:
~~~
Schema::create('users', function (Blueprint $table) {
$table->increments('id');
});
~~~
當然,在創建數據表的時候,可以使用任何數據庫結構生成器的?[字段方法](http://www.hmoore.net/tonyyu/laravel_5_6/786263#_205)?來定義數據表的字段。
#### 檢查數據表 / 字段是否存在
可以使用?`hasTable`?和?`hasColumn`?方法來檢查數據表或字段是否存在:
~~~
if (Schema::hasTable('users')) {
//
}
if (Schema::hasColumn('users', 'email')) {
//
}
~~~
#### 數據庫連接 & 表選項
如果要對非默認連接的數據庫連接執行結構操作,可以使用?`connection`?方法:
~~~
Schema::connection('foo')->create('users', function (Blueprint $table) {
$table->increments('id');
});
~~~
你可以在數據庫結構生成器上使用以下命令來定義表的選項:
| 命令 | 描述 |
| --- | --- |
| `$table->engine = 'InnoDB';` | 指定表存儲引擎 (MySQL)。 |
| `$table->charset = 'utf8';` | 指定數據表的默認字符集 (MySQL)。 |
| `$table->collation = 'utf8_unicode_ci';` | 指定數據表默認的排序規則 (MySQL)。 |
| `$table->temporary();` | 創建臨時表 (不支持SQL Server)。 |
### 重命名 / 刪除數據表
若要重命名數據表,可以使用?`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');
});
~~~
#### 可用的字段類型
數據庫結構生成器包含構建表時可以指定的各種字段類型:
| Command | Description |
| --- | --- |
| `$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');` | 相當于帶時區 DATETIME |
| `$table->decimal('amount', 8, 2);` | 相當于帶有精度與基數 DECIMAL |
| `$table->double('amount', 8, 2);` | 相當于帶有精度與基數 DOUBLE |
| `$table->enum('level', ['easy', 'hard']);` | 相當于 ENUM |
| `$table->float('amount', 8, 2);` | 相當于帶有精度與基數 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();` | 相當于為軟刪除添加一個可空的 帶時區的?`deleted_at`?字段 |
| `$table->string('name', 100);` | 相當于帶長度的 VARCHAR |
| `$table->text('description');` | 相當于 TEXT |
| `$table->time('sunrise');` | 相當于 TIME |
| `$table->timeTz('sunrise');` | 相當于帶時區的 TIME |
| `$table->timestamp('added_on');` | 相當于 TIMESTAMP |
| `$table->timestampTz('added_on');` | 相當于帶時區的 TIMESTAMP |
| `$table->timestamps();` | 相當于可空的?`created_at`?和?`updated_at`?TIMESTAMP |
| `$table->timestampsTz();` | 相當于可空且帶時區的?`created_at`?和?`updated_at`TIMESTAMP |
| `$table->tinyIncrements('id');` | 相當于自動遞增 UNSIGNED TINYINT |
| `$table->tinyInteger('votes');` | 相當于 TINYINT |
| `$table->unsignedBigInteger('votes');` | 相當于 Unsigned BIGINT |
| `$table->unsignedDecimal('amount', 8, 2);` | 相當于帶有精度和基數的 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 |
### 字段修飾
除了上述列出的字段類型之外, 還有幾個可以在添加字段時使用的
"修飾符" 。例如,如果要把字段設置為 "可空",就使用?`nullable`?方法:
~~~
Schema::table('users', function (Blueprint $table) {
$table->string('email')->nullable();
});
~~~
以下是所有可用的字段修飾符的列表。此列表不包括?[索引修飾符](http://www.hmoore.net/tonyyu/laravel_5_6/786263#_380):
| Modifier | Description |
| --- | --- |
| `->after('column')` | 將此字段放置在其它字段 "之后" (MySQL) |
| `->autoIncrement()` | 將 INTEGER 類型的字段設置為自動遞增的主鍵 |
| `->charset('utf8')` | 指定一個字符集 (MySQL) |
| `->collation('utf8_unicode_ci')` | 指定列的排序規則 (MySQL/SQL Server) |
| `->comment('my comment')` | 為字段增加注釋 (MySQL) |
| `->default($value)` | 為字段指定 "默認" 值 |
| `->first()` | 將此字段放置在數據表的 "首位" (MySQL) |
| `->nullable($value = true)` | 此字段允許寫入 NULL 值(默認情況下) |
| `->storedAs($expression)` | 創建一個存儲生成的字段 (MySQL) |
| `->unsigned()` | 設置 INTEGER 類型的字段為 UNSIGNED (MySQL) |
| `->useCurrent()` | 將 TIMESTAMP 類型的字段設置為使用 CURRENT_TIMESTAMP 作為默認值 |
| `->virtualAs($expression)` | 創建一個虛擬生成的字段 (MySQL) |
### 修改字段
#### 先決條件
在修改字段之前,請確保將?`doctrine/dbal`?依賴添加到?`composer.json`?文件中。Doctrine DBAL 庫用于確定字段的當前狀態, 并創建對該字段進行指定調整所需的 SQL 查詢:
~~~
composer require doctrine/dbal
~~~
#### 更新字段屬性
`change`?方法可以將現有的字段類型修改為新的類型或修改屬性。 比如,你可能想增加。字符串字段的長度,可以使用?`change`?方法把?`name`?字段的長度從 25 增加到 50:
~~~
Schema::table('users', function (Blueprint $table) {
$table->string('name', 50)->change();
});
~~~
我們應該將字段修改為可空:
~~~
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。
#### 重命名字段
可以使用結構生成器上的?`renameColumn`?方法來重命名字段。在重命名字段前, 請確保你的?`composer.json`?文件內已經加入?`doctrine/dbal`?依賴:
~~~
Schema::table('users', function (Blueprint $table) {
$table->renameColumn('from', 'to');
});
~~~
> {注} 當前不支持?`enum`?類型的字段重命名。
### 刪除字段
可以使用結構生成器上的?`dropColumn`?方法來刪除字段。 在從 SQLite 數據庫刪除字段前,你需要在?`composer.json`?文件中加入`doctrine/dbal`?依賴并在終端執行?`composer update`?來安裝該依賴:
~~~
Schema::table('users', function (Blueprint $table) {
$table->dropColumn('votes');
});
~~~
你可以傳遞一個字段數組給?`dropColumn`?方法來刪除多個字段:
~~~
Schema::table('users', function (Blueprint $table) {
$table->dropColumn(['votes', 'avatar', 'location']);
});
~~~
> {注} 不支持在使用 SQLite 數據庫時在單個遷移中刪除或修改多個字段。
#### 可用的命令別名
每個索引方法都可以接收一個可選的第二個參數來指定索引的名稱。 如果省略,名稱將從表和列的名稱派生。
| Command | Description |
| --- | --- |
| `$table->dropRememberToken();` | 刪除?`remember_token`字段。 |
| `$table->dropSoftDeletes();` | 刪除?`deleted_at`?字段。 |
| `$table->dropSoftDeletesTz();` | `dropSoftDeletes()`?方法的別名 |
| `$table->dropTimestamps();` | 刪除?`created_at`?和?`updated_at`?字段。 |
| `$table->dropTimestampsTz();` | `dropTimestamps()`?方法的別名。 |
## 索引
### 創建索引
結構生成器支持多種類型的索引。首先,先指定字段值唯一,即簡單地在字段定義 之后鏈式調用?`unique`?方法來創建索引,例如:
~~~
$table->string('email')->unique();
~~~
或者,你也可以在定義完字段之后創建索引。例如:
~~~
$table->unique('email');
~~~
你甚至可以將數組傳遞給索引方法來創建一個復合(或合成)索引:
~~~
$table->index(['account_id', 'created_at']);
~~~
Laravel 會自動生成一個合理的索引名稱,但你也可以傳遞第二個參數來自定義索引名稱:
~~~
$table->unique('email', 'unique_email');
~~~
#### 可用的索引類型
| 命令 | 描述 |
| --- | --- |
| `$table->primary('id');` | 添加主鍵 |
| `$table->primary(['id', 'parent_id']);` | 添加復合鍵 |
| `$table->unique('email');` | 添加唯一索引 |
| `$table->index('state');` | 添加普通索引 |
| `$table->spatialIndex('location');` | 添加空間索引 ( SQLite 除外) |
#### 索引長度 & MySQL / MariaDB
Laravel 默認使用?`utf8mb4`?字符,它支持在數據庫中存儲 "emojis" 。 如果你是在版本低于 5.7.7 的 MySQL release 或者版本低于 10.2.2 的 MariaDB release 上創建索引,那就需要你手動配置遷移生成的默認字符串長度。 即在?`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" 表中刪除基本索引 |
| `$table->dropSpatialIndex('geo_location_spatialindex');` | 從 "geo" 表中刪除空間索引 ( SQLite 除外). |
如果將字段數組傳遞給?`dropIndex`?方法,會刪除根據表名、字段和鍵類型生成的索引名稱:
~~~
Schema::table('geo', function (Blueprint $table) {
$table->dropIndex(['state']); // 刪除索引 'geo_state_index'
});
~~~
### 外鍵約束
Laravel 還支持創建用于在數據庫層中的強制引用完整性的外鍵約束。 例如,讓我們在?`posts`?表上定義一個引用?`users`?表的?`id`?字段的?`user_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
- 基礎功能
- 路由
- 中間件
- CSRF 保護
- 控制器
- 請求
- 響應
- 視圖
- URL
- Session
- 表單驗證
- 錯誤
- 日志
- 前端開發
- Blade 模板
- 本地化
- 前端指南
- 編輯資源 Mix
- 安全相關
- 用戶認證
- Passport OAuth 認證
- 用戶授權
- 加密解密
- 哈希
- 重置密碼
- 綜合話題
- Artisan 命令行
- 廣播系統
- 緩存系統
- 集合
- 事件系統
- 文件存儲
- 輔助函數
- 郵件發送
- 消息通知
- 擴展包開發
- 隊列
- 任務調度
- 數據庫
- 快速入門
- 查詢構造器
- 分頁
- 數據庫遷移
- 數據填充
- Redis
- Eloquent ORM
- 快速入門
- 模型關聯
- Eloquent 集合
- 修改器
- API 資源
- 序列化
- 測試相關
- 快速入門
- HTTP 測試
- 瀏覽器測試 Dusk
- 數據庫測試
- 測試模擬器
- 官方擴展包
- Cashier 交易工具包
- Envoy 部署工具
- Horizon
- Scout 全文搜索
- Socialite 社會化登錄