数据库:迁移
简介
迁移就像数据库的版本控制,让团队能够定义并共享应用的数据库结构。如果你曾经在从版本控制拉取改动后,还得告诉同事在本地数据库里手动加一列,那你就已经遇到了数据库迁移要解决的问题。
Laravel 的 Schema facade 为所有 Laravel 支持的数据库系统提供了与具体数据库无关的建表与改表能力。通常,迁移会使用该 facade 来创建和修改数据库表与列。
生成迁移
你可以使用 make:migration Artisan 命令 生成数据库迁移。新迁移会放在 database/migrations 目录中。每个迁移文件名都包含时间戳,以便 Laravel 确定迁移的执行顺序:
php artisan make:migration create_flights_tableLaravel 会根据迁移名称尝试推断表名,以及该迁移是否在创建新表。若能从迁移名确定表名,Laravel 会在生成的迁移文件中预填该表;否则,你可以在迁移文件中手动指定表名。
若希望为生成的迁移指定自定义路径,可在执行 make:migration 时使用 --path 选项。给定路径应相对于应用的根路径。
INFO
迁移 stub 可通过 发布 stub 进行自定义。
压缩迁移
随着应用不断构建,迁移会越积越多,database/migrations 目录可能膨胀到成百上千个迁移文件。若有需要,你可以将迁移「压缩」成单个 SQL 文件。首先执行 schema:dump 命令:
php artisan schema:dump
# Dump the current database schema and prune all existing migrations...
php artisan schema:dump --prune执行该命令后,Laravel 会向应用的 database/schema 目录写入一个「schema」文件,文件名与数据库连接对应。之后当你迁移数据库且尚未执行过其他迁移时,Laravel 会先执行你当前所用数据库连接对应 schema 文件中的 SQL 语句;执行完 schema 文件后,再执行未被纳入该次导出的剩余迁移。
若应用的测试使用的数据库连接与本地开发常用连接不同,应确保也用该连接导出过 schema 文件,以便测试能正确构建数据库。你可以在导出本地开发常用连接之后再这样做:
php artisan schema:dump
php artisan schema:dump --database=testing --prune应将数据库 schema 文件提交到版本控制,以便团队中的新成员能快速创建应用的初始数据库结构。
WARNING
迁移压缩仅适用于 MariaDB、MySQL、PostgreSQL 与 SQLite,并且会使用各数据库的命令行客户端。
迁移结构
迁移类包含两个方法:up 与 down。up 用于向数据库添加新表、列或索引,而 down 应撤销 up 所做的操作。
在这两个方法中,你都可以使用 Laravel schema 构建器以富有表现力的方式创建和修改表。要了解 Schema 构建器上的全部可用方法,请参阅其文档。例如,下面的迁移会创建 flights 表:
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('flights', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('airline');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::drop('flights');
}
};设置迁移连接
若迁移将使用应用默认数据库连接以外的连接,应设置迁移的 $connection 属性:
/**
* The database connection that should be used by the migration.
*
* @var string
*/
protected $connection = 'pgsql';
/**
* Run the migrations.
*/
public function up(): void
{
// ...
}运行迁移
要运行所有尚未执行的迁移,执行 migrate Artisan 命令:
php artisan migrate若想查看目前已运行哪些迁移,可使用 migrate:status Artisan 命令:
php artisan migrate:status若只想查看迁移将执行的 SQL 而不实际运行,可向 migrate 命令提供 --pretend 标志:
php artisan migrate --pretend隔离迁移执行
若你在多台服务器上部署应用,并在部署流程中运行迁移,通常不希望两台服务器同时迁移数据库。为避免这种情况,可在调用 migrate 时使用 isolated 选项。
提供 isolated 选项后,Laravel 会在尝试运行迁移前,通过应用的缓存驱动获取原子锁。在持有该锁期间,其他运行 migrate 的尝试将不会执行;但命令仍会以成功的退出状态码结束:
php artisan migrate --isolatedWARNING
要使用此功能,应用的默认缓存驱动必须是 memcached、redis、dynamodb、database、file 或 array。此外,所有服务器都必须与同一台中央缓存服务器通信。
在生产环境强制运行迁移
部分迁移操作具有破坏性,可能导致数据丢失。为防止误对生产数据库执行这些命令,执行前会提示确认。若要跳过提示强制执行,请使用 --force 标志:
php artisan migrate --force回滚迁移
要回滚最近一次迁移操作,可使用 rollback Artisan 命令。该命令会回滚最后一批迁移,其中可能包含多个迁移文件:
php artisan migrate:rollback可通过向 rollback 命令提供 step 选项来回滚有限数量的迁移。例如,下面的命令会回滚最近五次迁移:
php artisan migrate:rollback --step=5可通过向 rollback 命令提供 batch 选项来回滚特定「批次」的迁移,其中 batch 对应应用 migrations 数据表中的批次值。例如,下面的命令会回滚第三批中的所有迁移:
php artisan migrate:rollback --batch=3若只想查看迁移回滚将执行的 SQL 而不实际运行,可向 migrate:rollback 命令提供 --pretend 标志:
php artisan migrate:rollback --pretendmigrate:reset 命令会回滚应用的全部迁移:
php artisan migrate:reset用一条命令回滚并迁移
migrate:refresh 命令会先回滚全部迁移,再执行 migrate。这实际上会重建整个数据库:
php artisan migrate:refresh
# Refresh the database and run all database seeds...
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默认情况下,migrate:fresh 只删除默认数据库连接上的表。不过,你可以使用 --database 选项指定要迁移的数据库连接。连接名应与应用 database 配置文件 中定义的连接对应:
php artisan migrate:fresh --database=adminWARNING
migrate:fresh 会删除所有数据库表,无论其前缀如何。在与其他应用共享的数据库上开发时,请谨慎使用该命令。
表
创建表
要创建新的数据库表,请使用 Schema facade 的 create 方法。create 接受两个参数:第一个是表名,第二个是闭包,闭包会收到可用于定义新表的 Blueprint 对象:
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('email');
$table->timestamps();
});
创建表时,可使用 schema 构建器的任意列方法来定义表的列。
判断表 / 列是否存在
你可以使用 hasTable、hasColumn 与 hasIndex 方法判断表、列或索引是否存在:
if (Schema::hasTable('users')) {
// The "users" table exists...
}
if (Schema::hasColumn('users', 'email')) {
// The "users" table exists and has an "email" column...
}
if (Schema::hasIndex('users', ['email'], 'unique')) {
// The "users" table exists and has a unique index on the "email" column...
}
数据库连接与表选项
若要在非应用默认连接的数据库连接上执行 schema 操作,请使用 connection 方法:
Schema::connection('sqlite')->create('users', function (Blueprint $table) {
$table->id();
});
此外,还有一些属性和方法可用于定义表创建的其他方面。使用 MariaDB 或 MySQL 时,可用 engine 属性指定表的存储引擎:
Schema::create('users', function (Blueprint $table) {
$table->engine('InnoDB');
// ...
});
使用 MariaDB 或 MySQL 时,可用 charset 与 collation 属性指定所创建表的字符集与排序规则:
Schema::create('users', function (Blueprint $table) {
$table->charset('utf8mb4');
$table->collation('utf8mb4_unicode_ci');
// ...
});
temporary 方法可用来表明该表应为「临时」表。临时表仅对当前连接的数据库会话可见,并在连接关闭时自动删除:
Schema::create('calculations', function (Blueprint $table) {
$table->temporary();
// ...
});
若要为数据库表添加「注释」,可在表实例上调用 comment 方法。表注释目前仅受 MariaDB、MySQL 与 PostgreSQL 支持:
Schema::create('calculations', function (Blueprint $table) {
$table->comment('Business calculations');
// ...
});
更新表
Schema facade 的 table 方法可用于更新已有表。与 create 类似,table 接受两个参数:表名,以及接收 Blueprint 实例的闭包,你可用它向表添加列或索引:
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
Schema::table('users', function (Blueprint $table) {
$table->integer('votes');
});
重命名 / 删除表
要重命名已有数据库表,请使用 rename 方法:
use Illuminate\Support\Facades\Schema;
Schema::rename($from, $to);
要删除已有表,可使用 drop 或 dropIfExists 方法:
Schema::drop('users');
Schema::dropIfExists('users');
重命名带外键的表
重命名表之前,应确认该表上的外键约束在迁移文件中都有显式名称,而不是让 Laravel 按约定自动命名。否则,外键约束名仍会引用旧表名。
列
创建列
Schema facade 的 table 方法可用于更新已有表。与 create 类似,table 接受两个参数:表名,以及接收 Illuminate\Database\Schema\Blueprint 实例的闭包,你可用它向表添加列:
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
Schema::table('users', function (Blueprint $table) {
$table->integer('votes');
});
可用列类型
schema 构建器的 blueprint 提供了多种与可向数据库表添加的列类型相对应的方法。可用方法列于下表:
布尔类型
字符串与文本类型
数值类型
日期与时间类型
二进制类型
对象与 JSON 类型
UUID 与 ULID 类型
空间类型
关联类型
特殊类型
bigIncrements() {.collection-method .first-collection-method}
bigIncrements 方法会创建自增的、与 UNSIGNED BIGINT(主键)等价的列:
$table->bigIncrements('id');
bigInteger() {.collection-method}
bigInteger 方法会创建与 BIGINT 等价的列:
$table->bigInteger('votes');
binary() {.collection-method}
binary 方法会创建与 BLOB 等价的列:
$table->binary('photo');
使用 MySQL、MariaDB 或 SQL Server 时,可传入 length 与 fixed 参数以创建与 VARBINARY 或 BINARY 等价的列:
$table->binary('data', length: 16); // VARBINARY(16)
$table->binary('data', length: 16, fixed: true); // BINARY(16)
boolean() {.collection-method}
boolean 方法会创建与 BOOLEAN 等价的列:
$table->boolean('confirmed');
char() {.collection-method}
char 方法会创建指定长度的、与 CHAR 等价的列:
$table->char('name', length: 100);
dateTimeTz() {.collection-method}
dateTimeTz 方法会创建与 DATETIME(含时区)等价的列,并可指定小数秒精度:
$table->dateTimeTz('created_at', precision: 0);
dateTime() {.collection-method}
dateTime 方法会创建与 DATETIME 等价的列,并可指定小数秒精度:
$table->dateTime('created_at', precision: 0);
date() {.collection-method}
date 方法会创建与 DATE 等价的列:
$table->date('created_at');
decimal() {.collection-method}
decimal 方法会创建与 DECIMAL 等价的列,并指定精度(总位数)与小数位数:
$table->decimal('amount', total: 8, places: 2);
double() {.collection-method}
double 方法会创建与 DOUBLE 等价的列:
$table->double('amount');
enum() {.collection-method}
enum 方法会创建带有给定合法取值的、与 ENUM 等价的列:
$table->enum('difficulty', ['easy', 'hard']);
float() {.collection-method}
float 方法会创建指定精度的、与 FLOAT 等价的列:
$table->float('amount', precision: 53);
foreignId() {.collection-method}
foreignId 方法会创建与 UNSIGNED BIGINT 等价的列:
$table->foreignId('user_id');
foreignIdFor() {.collection-method}
foreignIdFor 方法会为给定模型类添加 {column}_id 等价列。列类型将根据模型主键类型为 UNSIGNED BIGINT、CHAR(36) 或 CHAR(26):
$table->foreignIdFor(User::class);
foreignUlid() {.collection-method}
foreignUlid 方法会创建与 ULID 等价的列:
$table->foreignUlid('user_id');
foreignUuid() {.collection-method}
foreignUuid 方法会创建与 UUID 等价的列:
$table->foreignUuid('user_id');
geography() {.collection-method}
geography 方法会创建带有给定空间类型与 SRID(空间参考系统标识符)的、与 GEOGRAPHY 等价的列:
$table->geography('coordinates', subtype: 'point', srid: 4326);
INFO
空间类型的支持取决于数据库驱动。请参阅所用数据库的文档。若应用使用 PostgreSQL,必须先安装 PostGIS 扩展,才能使用 geography 方法。
geometry() {.collection-method}
geometry 方法会创建带有给定空间类型与 SRID(空间参考系统标识符)的、与 GEOMETRY 等价的列:
$table->geometry('positions', subtype: 'point', srid: 0);
INFO
空间类型的支持取决于数据库驱动。请参阅所用数据库的文档。若应用使用 PostgreSQL,必须先安装 PostGIS 扩展,才能使用 geometry 方法。
id() {.collection-method}
id 方法是 bigIncrements 的别名。默认会创建名为 id 的列;若希望使用其他列名,可传入列名:
$table->id();
increments() {.collection-method}
increments 方法会创建自增的、与 UNSIGNED INTEGER 等价的列,并作为主键:
$table->increments('id');
integer() {.collection-method}
integer 方法会创建与 INTEGER 等价的列:
$table->integer('votes');
ipAddress() {.collection-method}
ipAddress 方法会创建与 VARCHAR 等价的列:
$table->ipAddress('visitor');
使用 PostgreSQL 时,会创建 INET 列。
json() {.collection-method}
json 方法会创建与 JSON 等价的列:
$table->json('options');
使用 SQLite 时,会创建 TEXT 列。
jsonb() {.collection-method}
jsonb 方法会创建与 JSONB 等价的列:
$table->jsonb('options');
使用 SQLite 时,会创建 TEXT 列。
longText() {.collection-method}
longText 方法会创建与 LONGTEXT 等价的列:
$table->longText('description');
使用 MySQL 或 MariaDB 时,可为列应用 binary 字符集,以创建与 LONGBLOB 等价的列:
$table->longText('data')->charset('binary'); // LONGBLOB
macAddress() {.collection-method}
macAddress 方法会创建用于存放 MAC 地址的列。某些数据库系统(如 PostgreSQL)对此类数据有专用列类型;其他系统则使用字符串等价列:
$table->macAddress('device');
mediumIncrements() {.collection-method}
mediumIncrements 方法会创建自增的、与 UNSIGNED MEDIUMINT 等价的列,并作为主键:
$table->mediumIncrements('id');
mediumInteger() {.collection-method}
mediumInteger 方法会创建与 MEDIUMINT 等价的列:
$table->mediumInteger('votes');
mediumText() {.collection-method}
mediumText 方法会创建与 MEDIUMTEXT 等价的列:
$table->mediumText('description');
使用 MySQL 或 MariaDB 时,可为列应用 binary 字符集,以创建与 MEDIUMBLOB 等价的列:
$table->mediumText('data')->charset('binary'); // MEDIUMBLOB
morphs() {.collection-method}
morphs 方法是便捷方法,会添加 {column}_id 等价列,以及 {column}_type 的 VARCHAR 等价列。{column}_id 的列类型将根据模型主键类型为 UNSIGNED BIGINT、CHAR(36) 或 CHAR(26)。
该方法用于定义多态 Eloquent 关联 所需的列。在下面的例子中,会创建 taggable_id 与 taggable_type 列:
$table->morphs('taggable');
nullableMorphs() {.collection-method}
该方法与 morphs 类似;不过所创建的列可为「可空」:
$table->nullableMorphs('taggable');
nullableUlidMorphs() {.collection-method}
该方法与 ulidMorphs 类似;不过所创建的列可为「可空」:
$table->nullableUlidMorphs('taggable');
nullableUuidMorphs() {.collection-method}
该方法与 uuidMorphs 类似;不过所创建的列可为「可空」:
$table->nullableUuidMorphs('taggable');
rememberToken() {.collection-method}
rememberToken 方法会创建可空的、与 VARCHAR(100) 等价的列,用于存储当前的「记住我」认证令牌:
$table->rememberToken();
set() {.collection-method}
set 方法会创建带有给定合法取值列表的、与 SET 等价的列:
$table->set('flavors', ['strawberry', 'vanilla']);
smallIncrements() {.collection-method}
smallIncrements 方法会创建自增的、与 UNSIGNED SMALLINT 等价的列,并作为主键:
$table->smallIncrements('id');
smallInteger() {.collection-method}
smallInteger 方法会创建与 SMALLINT 等价的列:
$table->smallInteger('votes');
softDeletesTz() {.collection-method}
softDeletesTz 方法会添加可空的 deleted_at、与 TIMESTAMP(含时区)等价的列,并可指定小数秒精度。该列用于存储 Eloquent「软删除」功能所需的 deleted_at 时间戳:
$table->softDeletesTz('deleted_at', precision: 0);
softDeletes() {.collection-method}
softDeletes 方法会添加可空的 deleted_at、与 TIMESTAMP 等价的列,并可指定小数秒精度。该列用于存储 Eloquent「软删除」功能所需的 deleted_at 时间戳:
$table->softDeletes('deleted_at', precision: 0);
string() {.collection-method}
string 方法会创建指定长度的、与 VARCHAR 等价的列:
$table->string('name', length: 100);
text() {.collection-method}
text 方法会创建与 TEXT 等价的列:
$table->text('description');
使用 MySQL 或 MariaDB 时,可为列应用 binary 字符集,以创建与 BLOB 等价的列:
$table->text('data')->charset('binary'); // BLOB
timeTz() {.collection-method}
timeTz 方法会创建与 TIME(含时区)等价的列,并可指定小数秒精度:
$table->timeTz('sunrise', precision: 0);
time() {.collection-method}
time 方法会创建与 TIME 等价的列,并可指定小数秒精度:
$table->time('sunrise', precision: 0);
timestampTz() {.collection-method}
timestampTz 方法会创建与 TIMESTAMP(含时区)等价的列,并可指定小数秒精度:
$table->timestampTz('added_at', precision: 0);
timestamp() {.collection-method}
timestamp 方法会创建与 TIMESTAMP 等价的列,并可指定小数秒精度:
$table->timestamp('added_at', precision: 0);
timestampsTz() {.collection-method}
timestampsTz 方法会创建 created_at 与 updated_at、与 TIMESTAMP(含时区)等价的列,并可指定小数秒精度:
$table->timestampsTz(precision: 0);
timestamps() {.collection-method}
timestamps 方法会创建 created_at 与 updated_at、与 TIMESTAMP 等价的列,并可指定小数秒精度:
$table->timestamps(precision: 0);
tinyIncrements() {.collection-method}
tinyIncrements 方法会创建自增的、与 UNSIGNED TINYINT 等价的列,并作为主键:
$table->tinyIncrements('id');
tinyInteger() {.collection-method}
tinyInteger 方法会创建与 TINYINT 等价的列:
$table->tinyInteger('votes');
tinyText() {.collection-method}
tinyText 方法会创建与 TINYTEXT 等价的列:
$table->tinyText('notes');
使用 MySQL 或 MariaDB 时,可为列应用 binary 字符集,以创建与 TINYBLOB 等价的列:
$table->tinyText('data')->charset('binary'); // TINYBLOB
unsignedBigInteger() {.collection-method}
unsignedBigInteger 方法会创建与 UNSIGNED BIGINT 等价的列:
$table->unsignedBigInteger('votes');
unsignedInteger() {.collection-method}
unsignedInteger 方法会创建与 UNSIGNED INTEGER 等价的列:
$table->unsignedInteger('votes');
unsignedMediumInteger() {.collection-method}
unsignedMediumInteger 方法会创建与 UNSIGNED MEDIUMINT 等价的列:
$table->unsignedMediumInteger('votes');
unsignedSmallInteger() {.collection-method}
unsignedSmallInteger 方法会创建与 UNSIGNED SMALLINT 等价的列:
$table->unsignedSmallInteger('votes');
unsignedTinyInteger() {.collection-method}
unsignedTinyInteger 方法会创建与 UNSIGNED TINYINT 等价的列:
$table->unsignedTinyInteger('votes');
ulidMorphs() {.collection-method}
ulidMorphs 方法是便捷方法,会添加 {column}_id 的 CHAR(26) 等价列,以及 {column}_type 的 VARCHAR 等价列。
该方法用于定义使用 ULID 标识符的多态 Eloquent 关联 所需的列。在下面的例子中,会创建 taggable_id 与 taggable_type 列:
$table->ulidMorphs('taggable');
uuidMorphs() {.collection-method}
uuidMorphs 方法是便捷方法,会添加 {column}_id 的 CHAR(36) 等价列,以及 {column}_type 的 VARCHAR 等价列。
该方法用于定义使用 UUID 标识符的多态 Eloquent 关联 所需的列。在下面的例子中,会创建 taggable_id 与 taggable_type 列:
$table->uuidMorphs('taggable');
ulid() {.collection-method}
ulid 方法会创建与 ULID 等价的列:
$table->ulid('id');
uuid() {.collection-method}
uuid 方法会创建与 UUID 等价的列:
$table->uuid('id');
vector() {.collection-method}
vector 方法会创建与 vector 等价的列:
$table->vector('embedding', dimensions: 100);
year() {.collection-method}
year 方法会创建与 YEAR 等价的列:
$table->year('birth_year');
列修饰符
除了上面列出的列类型外,向数据库表添加列时还可使用若干列「修饰符」。例如,要让列「可空」,可使用 nullable 方法:
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
Schema::table('users', function (Blueprint $table) {
$table->string('email')->nullable();
});
下表列出了全部可用的列修饰符。本列表不包含索引修饰符:
| Modifier | 描述 |
|---|---|
->after('column') | Place the column "after" another column (MariaDB / MySQL). |
->autoIncrement() | Set INTEGER columns as auto-incrementing (primary key). |
->charset('utf8mb4') | Specify a character set for the column (MariaDB / MySQL). |
->collation('utf8mb4_unicode_ci') | Specify a collation for the column. |
->comment('my comment') | Add a comment to a column (MariaDB / MySQL / PostgreSQL). |
->default($value) | Specify a "default" value for the column. |
->first() | Place the column "first" in the table (MariaDB / MySQL). |
->from($integer) | Set the starting value of an auto-incrementing field (MariaDB / MySQL / PostgreSQL). |
->invisible() | Make the column "invisible" to SELECT * queries (MariaDB / MySQL). |
->nullable($value = true) | Allow NULL values to be inserted into the column. |
->storedAs($expression) | Create a stored generated column (MariaDB / MySQL / PostgreSQL / SQLite). |
->unsigned() | Set INTEGER columns as UNSIGNED (MariaDB / MySQL). |
->useCurrent() | Set TIMESTAMP columns to use CURRENT_TIMESTAMP as default value. |
->useCurrentOnUpdate() | Set TIMESTAMP columns to use CURRENT_TIMESTAMP when a record is updated (MariaDB / MySQL). |
->virtualAs($expression) | Create a virtual generated column (MariaDB / MySQL / SQLite). |
->generatedAs($expression) | Create an identity column with specified sequence options (PostgreSQL). |
->always() | Defines the precedence of sequence values over input for an identity column (PostgreSQL). |
默认表达式
default 修饰符可接受一个值,或一个 Illuminate\Database\Query\Expression 实例。使用 Expression 实例可阻止 Laravel 给值加引号,从而允许使用特定于数据库的函数。在需要为 JSON 列指定默认值时,这一点尤其有用:
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Query\Expression;
use Illuminate\Database\Migrations\Migration;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('flights', function (Blueprint $table) {
$table->id();
$table->json('movies')->default(new Expression('(JSON_ARRAY())'));
$table->timestamps();
});
}
};WARNING
默认表达式的支持取决于数据库驱动、数据库版本以及字段类型。请参阅所用数据库的文档。
列顺序
使用 MariaDB 或 MySQL 时,可用 after 方法在 schema 中已有列之后添加列:
$table->after('password', function (Blueprint $table) {
$table->string('address_line1');
$table->string('address_line2');
$table->string('city');
});
修改列
change 方法允许你修改已有列的类型与属性。例如,你可能希望增大 string 列的长度。下面将 name 列从 25 增加到 50:只需定义列的新状态,然后调用 change 方法:
Schema::table('users', function (Blueprint $table) {
$table->string('name', 50)->change();
});
修改列时,必须显式包含你希望保留在列定义上的全部修饰符——任何缺失的属性都会被丢弃。例如,要保留 unsigned、default 与 comment 属性,修改列时必须显式调用每个修饰符:
Schema::table('users', function (Blueprint $table) {
$table->integer('votes')->unsigned()->default(1)->comment('my comment')->change();
});
change 方法不会更改列上的索引。因此,修改列时可用索引修饰符显式添加或删除索引:
// Add an index...
$table->bigIncrements('id')->primary()->change();
// Drop an index...
$table->char('postal_code', 10)->unique(false)->change();重命名列
要重命名列,可使用 schema 构建器提供的 renameColumn 方法:
Schema::table('users', function (Blueprint $table) {
$table->renameColumn('from', 'to');
});
删除列
要删除列,可在 schema 构建器上使用 dropColumn 方法:
Schema::table('users', function (Blueprint $table) {
$table->dropColumn('votes');
});
向 dropColumn 传入列名数组,可一次删除表中的多列:
Schema::table('users', function (Blueprint $table) {
$table->dropColumn(['votes', 'avatar', 'location']);
});
可用命令别名
Laravel 提供了若干与删除常见列类型相关的便捷方法。各方法说明见下表:
| Command | 描述 |
|---|---|
$table->dropMorphs('morphable'); | Drop the morphable_id and morphable_type columns. |
$table->dropRememberToken(); | Drop the remember_token column. |
$table->dropSoftDeletes(); | Drop the deleted_at column. |
$table->dropSoftDeletesTz(); | Alias of dropSoftDeletes() method. |
$table->dropTimestamps(); | Drop the created_at and updated_at columns. |
$table->dropTimestampsTz(); | Alias of dropTimestamps() method. |
索引
创建索引
Laravel schema 构建器支持多种索引类型。下面的例子创建新的 email 列,并指定其值应唯一。要创建索引,可在列定义上链式调用 unique 方法:
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
Schema::table('users', function (Blueprint $table) {
$table->string('email')->unique();
});
或者,你也可以在定义列之后再创建索引。为此,应在 schema 构建器 blueprint 上调用 unique 方法。该方法接受应建立唯一索引的列名:
$table->unique('email');
你甚至可以向索引方法传入列名数组,以创建复合(组合)索引:
$table->index(['account_id', 'created_at']);
创建索引时,Laravel 会根据表名、列名与索引类型自动生成索引名,但你也可以向方法传入第二个参数以自行指定索引名:
$table->unique('email', 'unique_email');
可用索引类型
Laravel 的 schema 构建器 blueprint 类为 Laravel 支持的每种索引类型都提供了创建方法。每个索引方法都接受可选的第二个参数以指定索引名;若省略,名称将由表名、用于索引的列名以及索引类型推导得出。可用索引方法见下表:
| Command | 描述 |
|---|---|
$table->primary('id'); | Adds a primary key. |
$table->primary(['id', 'parent_id']); | Adds composite keys. |
$table->unique('email'); | Adds a unique index. |
$table->index('state'); | Adds an index. |
$table->fullText('body'); | Adds a full text index (MariaDB / MySQL / PostgreSQL). |
$table->fullText('body')->language('english'); | Adds a full text index of the specified language (PostgreSQL). |
$table->spatialIndex('location'); | Adds a spatial index (except SQLite). |
重命名索引
要重命名索引,可使用 schema 构建器 blueprint 提供的 renameIndex 方法。该方法的第一个参数为当前索引名,第二个参数为期望的新名称:
$table->renameIndex('from', 'to')
删除索引
要删除索引,必须指定索引名。默认情况下,Laravel 会根据表名、被索引列名与索引类型自动分配索引名。示例如下:
| Command | 描述 |
|---|---|
$table->dropPrimary('users_id_primary'); | Drop a primary key from the "users" table. |
$table->dropUnique('users_email_unique'); | Drop a unique index from the "users" table. |
$table->dropIndex('geo_state_index'); | Drop a basic index from the "geo" table. |
$table->dropFullText('posts_body_fulltext'); | Drop a full text index from the "posts" table. |
$table->dropSpatialIndex('geo_location_spatialindex'); | Drop a spatial index from the "geo" table (except SQLite). |
若向删除索引的方法传入列名数组,将根据表名、列名与索引类型生成约定式索引名:
Schema::table('geo', function (Blueprint $table) {
$table->dropIndex(['state']); // Drops index 'geo_state_index'
});
外键约束
Laravel 还支持创建外键约束,用于在数据库层面强制引用完整性。例如,我们在 posts 表上定义引用 users 表 id 列的 user_id 列:
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
Schema::table('posts', function (Blueprint $table) {
$table->unsignedBigInteger('user_id');
$table->foreign('user_id')->references('id')->on('users');
});
由于这种写法较为冗长,Laravel 还提供了更简洁、基于约定的方法以改善开发体验。使用 foreignId 创建列时,上面的例子可改写为:
Schema::table('posts', function (Blueprint $table) {
$table->foreignId('user_id')->constrained();
});
foreignId 方法会创建与 UNSIGNED BIGINT 等价的列,而 constrained 方法会按约定推断被引用的表与列。若表名不符合 Laravel 约定,可手动传给 constrained。另外,也可一并指定生成索引应使用的名称:
Schema::table('posts', function (Blueprint $table) {
$table->foreignId('user_id')->constrained(
table: 'users', indexName: 'posts_user_id'
);
});
你还可以为约束的「on delete」与「on update」属性指定期望的行为:
$table->foreignId('user_id')
->constrained()
->onUpdate('cascade')
->onDelete('cascade');
这些动作也提供了另一种更富表现力的写法:
| Method | 描述 |
|---|---|
$table->cascadeOnUpdate(); | Updates should cascade. |
$table->restrictOnUpdate(); | Updates should be restricted. |
$table->nullOnUpdate(); | Updates should set the foreign key value to null. |
$table->noActionOnUpdate(); | No action on updates. |
$table->cascadeOnDelete(); | Deletes should cascade. |
$table->restrictOnDelete(); | Deletes should be restricted. |
$table->nullOnDelete(); | Deletes should set the foreign key value to null. |
$table->noActionOnDelete(); | Prevents deletes if child records exist. |
任何额外的列修饰符都必须在 constrained 方法之前调用:
$table->foreignId('user_id')
->nullable()
->constrained();
删除外键
要删除外键,可使用 dropForeign 方法,并将要删除的外键约束名作为参数传入。外键约束与索引使用相同的命名约定。换句话说,外键约束名由表名与约束中的列名组成,并带有 _foreign 后缀:
$table->dropForeign('posts_user_id_foreign');
或者,你也可以向 dropForeign 传入包含外键列名的数组。该数组会按 Laravel 的约束命名约定转换为外键约束名:
$table->dropForeign(['user_id']);
开关外键约束
你可以在迁移中使用以下方法启用或禁用外键约束:
Schema::enableForeignKeyConstraints();
Schema::disableForeignKeyConstraints();
Schema::withoutForeignKeyConstraints(function () {
// Constraints disabled within this closure...
});
WARNING
SQLite 默认禁用外键约束。使用 SQLite 时,请确保在尝试于迁移中创建外键之前,已在数据库配置中启用外键支持。
事件
为方便起见,每次迁移操作都会派发一个事件。下列事件均继承自基类 Illuminate\Database\Events\MigrationEvent:
| Class | 描述 |
|---|---|
Illuminate\Database\Events\MigrationsStarted | A batch of migrations is about to be executed. |
Illuminate\Database\Events\MigrationsEnded | A batch of migrations has finished executing. |
Illuminate\Database\Events\MigrationStarted | A single migration is about to be executed. |
Illuminate\Database\Events\MigrationEnded | A single migration has finished executing. |
Illuminate\Database\Events\NoPendingMigrations | A migration command found no pending migrations. |
Illuminate\Database\Events\SchemaDumped | A database schema dump has completed. |
Illuminate\Database\Events\SchemaLoaded | An existing database schema dump has loaded. |