簡介
所謂遷移就像是數據庫的版本控制,這種機制允許團隊簡單輕松的編輯并共享應用的數據庫表結構。遷移通常和 laravel 的 schema 構建器結對從而可以很容易地構建應用的數據庫表結構。如果你曾經頻繁告知團隊成員需要手動添加列到本地數據庫表結構以維護本地開發環境,那么這正是數據庫遷移所致力于解決的問題。
laravel 的 schema 門面提供了與數據庫系統無關的創建和操縱表的支持,在 laravel 所支持的所有數據庫系統中提供一致的、優雅的、流式的 api。
生成遷移
使用 artisan 命令 make:migration 就可以創建一個新的遷移:
1
|
php artisan make:migration create_users_table |
新的遷移位于 database/migrations 目錄下,每個遷移文件名都包含時間戳從而允許 laravel 判斷其順序。
--table 和 --create 選項可以用于指定表名以及該遷移是否要創建一個新的數據表。這些選項只需要簡單放在上述遷移命令后面并指定表名:
1
2
|
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 方法的逆操作,和 up 里的操作相反。
在這兩個方法中你都要用到 laravel 的 schema 構建器來創建和修改表,要了解更多 schema 構建器提供的方法,查看其文檔。下面讓我們先看看創建 flights 表的簡單示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
<?php use illuminate\support\facades\schema; use illuminate\database\schema\blueprint; use illuminate\database\migrations\migration; class createflightstable extends migration { /** * run the migrations. * * @return void */ public function up() { schema::create( 'flights' , function (blueprint $table ) { $table ->increments( 'id' ); $table ->string( 'name' ); $table ->string( 'airline' ); $table ->timestamps(); }); } /** * reverse the migrations. * * @return void */ public function down() { schema::drop( 'flights' ); } } |
運行遷移
要運行應用中所有未執行的遷移,可以使用 artisan 命令提供的 migrate 方法:
1
|
php artisan migrate |
注:如果你正在使用 homestead 虛擬機,需要在虛擬機中運行上面這條命令。
在生產環境中強制運行遷移
有些遷移操作是毀滅性的,這意味著它們可能造成數據的丟失,為了避免在生產環境數據庫中運行這些命令,你將會在運行這些命令之前被提示并確認。想要強制運行這些命令而不被提示,可以使用 --force 標記:
1
|
php artisan migrate --force |
回滾遷移
想要回滾最新的一次遷移”操作“,可以使用 rollback 命令,注意這將會回滾最后一批運行的遷移,可能包含多個遷移文件:
1
|
php artisan migrate:rollback |
你也可以通過 rollback 命令上提供的 step 選項來回滾指定數目的遷移,例如,下面的命令將會回滾最后五條遷移:
1
|
php artisan migrate:rollback --step=5 |
migrate:reset 命令將會回滾所有的應用遷移:
1
|
php artisan migrate:reset |
在單個命令中回滾 & 遷移
migrate:refresh 命令將會先回滾所有數據庫遷移,然后運行 migrate 命令。這個命令可以有效的重建整個數據庫:
1
2
3
4
|
php artisan migrate:refresh // 重建數據庫并填充數據... php artisan migrate:refresh --seed |
當然,你也可以回滾或重建指定數量的遷移 —— 通過 refresh 命令提供的 step 選項,例如,下面的命令將會回滾或重建最后五條遷移:
1
|
php artisan migrate:refresh --step=5 |
刪除所有表 & 遷移
migrate:fresh 命令將會先從數據庫中刪除所有表然后執行 migrate 命令:
1
2
3
|
php artisan migrate:fresh php artisan migrate:fresh --seed |
數據表
創建表
使用 schema 門面上的 create 方法來創建新的數據表。create 方法接收兩個參數,第一個是表名,第二個是獲取用于定義新表的 blueprint 對象的閉包:
1
2
3
|
schema::create( 'users' , function ( $table ) { $table ->increments( 'id' ); }); |
當然,創建新表的時候,可以使用 schema 構建器中的任意列方法來定義數據表的列。
檢查表/列是否存在
你可以輕松地使用 hastable 和 hascolumn 方法檢查表或列是否存在:
1
2
3
4
5
6
7
|
if (schema::hastable( 'users' )) { // } if (schema::hascolumn( 'users' , 'email' )) { // } |
數據庫連接 & 表選項
如果你想要在一個數據庫連接上執行表結構操作,而該數據庫連接并不是默認數據庫連接,可以使用 connection 方法:
1
2
3
|
schema::connection( 'foo' )->create( 'users' , function (blueprint $table ) { $table ->increments( 'id' ); }); |
要設置表的存儲引擎、字符編碼等選項,可以在 schema 構建器上使用如下命令:
命令 | 描述 |
---|---|
$table->engine = 'innodb'; | 指定表的存儲引擎(mysql) |
$table->charset = 'utf8'; | 指定數據表的默認字符集(mysql) |
$table->collation = 'utf8_unicode_ci'; | 指定數據表的字符序(mysql) |
$table->temporary(); | 創建臨時表(除sql server) |
重命名/刪除表
要重命名一個已存在的數據表,使用 rename 方法:
1
|
schema::rename( $from , $to ); |
要刪除一個已存在的數據表,可以使用 drop 或 dropifexists 方法:
1
2
|
schema::drop( 'users' ); schema::dropifexists( 'users' ); |
通過外鍵重命名表
在重命名表之前,需要驗證該表包含的外鍵在遷移文件中有明確的名字,而不是 laravel 基于慣例分配的名字。否則,外鍵約束名將會指向舊的數據表。
數據列
創建數據列
要更新一個已存在的表,使用 schema 門面上的 table 方法,和 create 方法一樣,table 方法接收兩個參數:表名和獲取用于添加列到表的 blueprint 實例的閉包:
1
2
3
|
schema::table( 'users' , function (blueprint $table ) { $table ->string( 'email' ); }); |
可用的數據列類型
當然,schema 構建器包含一系列你可以用來構建表的列類型:
命令 | 描述 |
---|---|
$table->bigincrements('id'); | 等同于自增 unsigned bigint(主鍵)列 |
$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('level', ['easy', 'hard']); | 等同于 enum 類型列 |
$table->float('amount', 8, 2); | 等同于 float 類型列,帶精度和總位數 |
$table->geometry('positions'); | 等同于 geometry 類型列 |
$table->geometrycollection('positions'); | 等同于 geometrycollection 類型列 |
$table->increments('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'); | 等同于自增 unsigned mediumint 類型列(主鍵) |
$table->mediuminteger('numbers'); | 等同于 mediumint 類型列 |
$table->mediumtext('description'); | 等同于 mediumtext 類型列 |
$table->morphs('taggable'); | 添加一個 unsigned integer 類型的 taggable_id 列和一個 varchar 類型的 taggable_type 列 |
$table->multilinestring('positions'); | 等同于 multilinestring 類型列 |
$table->multipoint('positions'); | 等同于 multipoint 類型列 |
$table->multipolygon('positions'); | 等同于 multipolygon 類型列 |
$table->nullablemorphs('taggable'); | morphs() 列的 nullable 版本 |
$table->nullabletimestamps(); | timestamps() 的別名 |
$table->point('position'); | 等同于 point 類型列 |
$table->polygon('positions'); | 等同于 polygon 類型列 |
$table->remembertoken(); | 等同于添加一個允許為空的 remember_token varchar(100) 列 |
$table->smallincrements('id'); | 等同于自增 unsigned smallint (主鍵)類型列 |
$table->smallinteger('votes'); | 等同于 smallint 類型列 |
$table->softdeletes(); | 新增一個允許為空的 deleted_at timestamp 列用于軟刪除 |
$table->softdeletestz(); | 新增一個允許為空的 deleted_at timestamp (帶時區)列用于軟刪除 |
$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('numbers'); | 等同于自增的 unsigned tinyint 類型列(主鍵) |
$table->tinyinteger('numbers'); | 等同于 tinyint 類型列 |
$table->unsignedbiginteger('votes'); | 等同于無符號的 bigint 類型列 |
$table->unsigneddecimal('amount', 8, 2); | 等同于 unsigned decimal 類型列,帶有總位數和精度 |
$table->unsignedinteger('votes'); | 等同于無符號的 integer 類型列 |
$table->unsignedmediuminteger('votes'); | 等同于無符號的 mediumint 類型列 |
$table->unsignedsmallinteger('votes'); | 等同于無符號的 smallint 類型列 |
$table->unsignedtinyinteger('votes'); | 等同于無符號的 tinyint 類型列 |
$table->uuid('id'); | 等同于 uuid 類型列 |
$table->year('birth_year'); | 等同于 year 類型列 |
列修改器
除了上面列出的數據列類型之外,在添加列的時候還可以使用一些其它的列“修改器”,例如,要使列允許為 null,可以使用 nullable 方法:
1
2
3
|
schema::table( 'users' , function (blueprint $table ) { $table ->string( 'email' )->nullable(); }); |
下面是所有可用的列修改器列表,該列表不包含索引修改器:
修改器 | 描述 |
---|---|
->after('column') | 將該列置于另一個列之后 (mysql) |
->autoincrement() | 設置 integer 列為自增主鍵 |
->charset('utf8') | 指定數據列字符集(mysql) |
->collation('utf8_unicode_ci') | 指定數據列字符序(mysql/sql server) |
->comment('my comment') | 添加注釋信息 |
->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 語句:
1
|
composer require doctrine/dbal |
更新列屬性
change 方法允許你修改已存在的列為新的類型,或者修改列的屬性。例如,你可能想要增加 字符串類型列的尺寸,下面讓我們將 name 列的尺寸從 25 增加到 50:
1
2
3
|
schema::table( 'users' , function (blueprint $table ) { $table ->string( 'name' , 50)->change(); }); |
我們還可以修改該列允許 null 值:
1
2
3
|
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 和 unsignedsmallinteger。
重命名列
要重命名一個列,可以使用表結構構建器上的 renamecolumn 方法,在重命名一個列之前,確保 doctrine/dbal 依賴已經添加到 composer.json 文件并且已經運行了 composer update 命令:
1
2
3
|
schema::table( 'users' , function (blueprint $table ) { $table ->renamecolumn( 'from' , 'to' ); }); |
注:暫不支持 enum 類型的列的修改和重命名。
刪除數據列
要刪除一個列,使用 schema 構建器上的 dropcolumn 方法,同樣,在此之前,確保已經安裝了 doctrine/dbal 依賴:
1
2
3
|
schema::table( 'users' , function (blueprint $table ) { $table ->dropcolumn( 'votes' ); }); |
你可以通過傳遞列名數組到 dropcolumn 方法以便可以一次從數據表中刪除多個列:
1
2
3
|
schema::table( 'users' , function (blueprint $table ) { $table ->dropcolumn([ 'votes' , 'avatar' , 'location' ]); }); |
注:sqlite 數據庫暫不支持在單個遷移中刪除或修改多個列。
有效的命令別名
命令 | 描述 |
---|---|
$table->dropremembertoken(); | 刪除 remember_token 列 |
$table->dropsoftdeletes(); | 刪除 deleted_at 列 |
$table->dropsoftdeletestz(); | dropsoftdeletes() 方法別名 |
$table->droptimestamps(); | 刪除 created_at 和 updated_at 列 |
$table->droptimestampstz(); | droptimestamps() 方法別名 |
索引
創建索引
schema 構建器支持多種類型的索引,首先,讓我們看一個指定列值為唯一索引的例子。要創建該索引,可以使用 unique 方法:
1
|
$table ->string( 'email' )->unique(); |
此外,你可以在定義列之后創建索引,例如:
1
|
$table ->unique( 'email' ); |
你甚至可以傳遞列名數組到索引方法來創建組合索引:
1
|
$table ->index([ 'account_id' , 'created_at' ]); |
laravel 會自動生成合理的索引名稱,不過你也可以傳遞第二個參數到該方法用于指定索引名稱:
1
|
$table ->index( '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 字符集,支持在數據庫中存儲 emoji 表情。如果你現在運行的 mysql 版本低于 5.7.7(或者低于 10.2.2 版本的 mariadb),需要手動配置遷移命令生成的默認字符串長度,以便 mysql 為它們創建索引。你可以通過在 appserviceprovider 中調用 schema::defaultstringlength 方法來完成配置:
1
2
3
4
5
6
7
8
9
10
11
12
|
use illuminate\support\facades\schema; /** * bootstrap any application services. * * @return void * @translator laravelacademy.org */ public function boot() { schema::defaultstringlength(191); } |
作為可選方案,你可以為數據庫啟用 innodb_large_prefix 選項,至于如何合理啟用這個選項,可以參考數據庫文檔說明。
重命名索引
要重命名一個索引,可以使用 renameindex 方法,這個方法接收當前索引名作為第一個參數以及修改后的索引名作為第二個參數:
1
|
$table ->renameindex( 'from' , 'to' ) |
刪除索引
要刪除索引,必須指定索引名。默認情況下,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) |
如果要傳遞數據列數組到刪除索引方法,那么相應的索引名稱將會通過數據表名、列和鍵類型來自動生成:
1
2
3
|
schema::table( 'geo' , function (blueprint $table ) { $table ->dropindex([ 'state' ]); // drops index 'geo_state_index' }); |
外鍵約束
laravel 還提供了創建外鍵約束的支持,用于在數據庫層面強制引用完整性。例如,我們在posts 表中定義了一個引用 users 表 id 列的 user_id 列:
1
2
3
4
|
schema::table( 'posts' , function (blueprint $table ) { $table ->integer( 'user_id' )->unsigned(); $table ->foreign( 'user_id' )->references( 'id' )->on( 'users' ); }); |
你還可以為約束的“on delete”和“on update”屬性指定期望的動作:
1
2
3
|
$table ->foreign( 'user_id' ) ->references( 'id' )->on( 'users' ) ->ondelete( 'cascade' ); |
要刪除一個外鍵,可以使用 dropforeign 方法。外鍵約束和索引使用同樣的命名規則 —— 連接表名、外鍵名然后加上“_foreign”后綴:
1
|
$table ->dropforeign( 'posts_user_id_foreign' ); |
或者,你還可以傳遞在刪除時會自動使用基于慣例的約束名數值數組:
1
|
$table ->dropforeign([ 'user_id' ]); |
你可以在遷移時通過以下方法啟用或關閉外鍵約束:
1
2
|
schema::enableforeignkeyconstraints(); schema::disableforeignkeyconstraints(); |
注:由于使用外鍵風險級聯刪除風險較高,一般情況下我們很少使用外鍵,而是通過代碼邏輯來實現級聯操作。
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。