Skip to content
全部文档

数据库:迁移

简介

迁移就像数据库的版本控制,让团队能够定义并共享应用的数据库结构。如果你曾经在从版本控制拉取改动后,还得告诉同事在本地数据库里手动加一列,那你就已经遇到了数据库迁移要解决的问题。

Laravel 的 Schema facade 为所有 Laravel 支持的数据库系统提供了与具体数据库无关的建表与改表能力。通常,迁移会使用该 facade 来创建和修改数据库表与列。

生成迁移

你可以使用 make:migration Artisan 命令 生成数据库迁移。新迁移会放在 database/migrations 目录中。每个迁移文件名都包含时间戳,以便 Laravel 确定迁移的执行顺序:

shell
php artisan make:migration create_flights_table

Laravel 会根据迁移名称尝试推断表名,以及该迁移是否在创建新表。若能从迁移名确定表名,Laravel 会在生成的迁移文件中预填该表;否则,你可以在迁移文件中手动指定表名。

若希望为生成的迁移指定自定义路径,可在执行 make:migration 时使用 --path 选项。给定路径应相对于应用的根路径。

INFO

迁移 stub 可通过 发布 stub 进行自定义。

压缩迁移

随着应用不断构建,迁移会越积越多,database/migrations 目录可能膨胀到成百上千个迁移文件。若有需要,你可以将迁移「压缩」成单个 SQL 文件。首先执行 schema:dump 命令:

shell
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 文件,以便测试能正确构建数据库。你可以在导出本地开发常用连接之后再这样做:

shell
php artisan schema:dump
php artisan schema:dump --database=testing --prune

应将数据库 schema 文件提交到版本控制,以便团队中的新成员能快速创建应用的初始数据库结构。

WARNING

迁移压缩仅适用于 MariaDB、MySQL、PostgreSQL 与 SQLite,并且会使用各数据库的命令行客户端。

迁移结构

迁移类包含两个方法:updownup 用于向数据库添加新表、列或索引,而 down 应撤销 up 所做的操作。

在这两个方法中,你都可以使用 Laravel schema 构建器以富有表现力的方式创建和修改表。要了解 Schema 构建器上的全部可用方法,请参阅其文档。例如,下面的迁移会创建 flights 表:

php
<?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 属性:

php
/**
 * The database connection that should be used by the migration.
 *
 * @var string
 */
protected $connection = 'pgsql';

/**
 * Run the migrations.
 */
public function up(): void
{
    // ...
}

跳过迁移

有时某个迁移是为尚未启用的功能准备的,你暂时不希望它运行。此时可在迁移上定义 shouldRun 方法。若 shouldRun 返回 false,该迁移将被跳过:

php
use App\Models\Flight;
use Laravel\Pennant\Feature;

/**
 * Determine if this migration should run.
 */
public function shouldRun(): bool
{
    return Feature::active(Flight::class);
}

运行迁移

要运行所有尚未执行的迁移,执行 migrate Artisan 命令:

shell
php artisan migrate

若想查看哪些迁移已运行、哪些仍待执行,可使用 migrate:status Artisan 命令:

shell
php artisan migrate:status

若向 migrate 命令提供 --step 选项,命令会将每个迁移作为独立批次运行,之后你可以用 migrate:rollback 逐个回滚:

shell
php artisan migrate --step

若只想查看迁移将执行的 SQL 而不实际运行,可向 migrate 命令提供 --pretend 标志:

shell
php artisan migrate --pretend

隔离迁移执行

若你在多台服务器上部署应用,并在部署流程中运行迁移,通常不希望两台服务器同时迁移数据库。为避免这种情况,可在调用 migrate 时使用 isolated 选项。

提供 isolated 选项后,Laravel 会在尝试运行迁移前,通过应用的缓存驱动获取原子锁。在持有该锁期间,其他运行 migrate 的尝试将不会执行;但命令仍会以成功的退出状态码结束:

shell
php artisan migrate --isolated

WARNING

要使用此功能,应用的默认缓存驱动必须是 memcachedredisdynamodbdatabasefilearray。此外,所有服务器都必须与同一台中央缓存服务器通信。

在生产环境强制运行迁移

部分迁移操作具有破坏性,可能导致数据丢失。为防止误对生产数据库执行这些命令,执行前会提示确认。若要跳过提示强制执行,请使用 --force 标志:

shell
php artisan migrate --force

回滚迁移

要回滚最近一次迁移操作,可使用 rollback Artisan 命令。该命令会回滚最后一批迁移,其中可能包含多个迁移文件:

shell
php artisan migrate:rollback

可通过向 rollback 命令提供 step 选项来回滚有限数量的迁移。例如,下面的命令会回滚最近五次迁移:

shell
php artisan migrate:rollback --step=5

可通过向 rollback 命令提供 batch 选项来回滚特定「批次」的迁移,其中 batch 对应应用 migrations 数据表中的批次值。例如,下面的命令会回滚第三批中的所有迁移:

shell
php artisan migrate:rollback --batch=3

若只想查看迁移回滚将执行的 SQL 而不实际运行,可向 migrate:rollback 命令提供 --pretend 标志:

shell
php artisan migrate:rollback --pretend

migrate:reset 命令会回滚应用的全部迁移:

shell
php artisan migrate:reset

用一条命令回滚并迁移

migrate:refresh 命令会先回滚全部迁移,再执行 migrate。这实际上会重建整个数据库:

shell
php artisan migrate:refresh

# Refresh the database and run all database seeds...
php artisan migrate:refresh --seed

可通过向 refresh 命令提供 step 选项,回滚并重新迁移有限数量的迁移。例如,下面的命令会回滚并重新迁移最近五次迁移:

shell
php artisan migrate:refresh --step=5

删除所有表并迁移

migrate:fresh 命令会删除数据库中的所有表,然后执行 migrate

shell
php artisan migrate:fresh

php artisan migrate:fresh --seed

默认情况下,migrate:fresh 只删除默认数据库连接上的表。不过,你可以使用 --database 选项指定要迁移的数据库连接。连接名应与应用 database 配置文件 中定义的连接对应:

shell
php artisan migrate:fresh --database=admin

WARNING

migrate:fresh 会删除所有数据库表,无论其前缀如何。在与其他应用共享的数据库上开发时,请谨慎使用该命令。

创建表

要创建新的数据库表,请使用 Schema facade 的 create 方法。create 接受两个参数:第一个是表名,第二个是闭包,闭包会收到可用于定义新表的 Blueprint 对象:

php
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 构建器的任意列方法来定义表的列。

判断表 / 列是否存在

你可以使用 hasTablehasColumnhasIndex 方法判断表、列或索引是否存在:

php
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 方法:

php
Schema::connection('sqlite')->create('users', function (Blueprint $table) {
    $table->id();
});

此外,还有一些属性和方法可用于定义表创建的其他方面。使用 MariaDB 或 MySQL 时,可用 engine 属性指定表的存储引擎:

php
Schema::create('users', function (Blueprint $table) {
    $table->engine('InnoDB');

    // ...
});

使用 MariaDB 或 MySQL 时,可用 charsetcollation 属性指定所创建表的字符集与排序规则:

php
Schema::create('users', function (Blueprint $table) {
    $table->charset('utf8mb4');
    $table->collation('utf8mb4_unicode_ci');

    // ...
});

temporary 方法可用来表明该表应为「临时」表。临时表仅对当前连接的数据库会话可见,并在连接关闭时自动删除:

php
Schema::create('calculations', function (Blueprint $table) {
    $table->temporary();

    // ...
});

若要为数据库表添加「注释」,可在表实例上调用 comment 方法。表注释目前仅受 MariaDB、MySQL 与 PostgreSQL 支持:

php
Schema::create('calculations', function (Blueprint $table) {
    $table->comment('Business calculations');

    // ...
});

更新表

Schema facade 的 table 方法可用于更新已有表。与 create 类似,table 接受两个参数:表名,以及接收 Blueprint 实例的闭包,你可用它向表添加列或索引:

php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

Schema::table('users', function (Blueprint $table) {
    $table->integer('votes');
});

重命名 / 删除表

要重命名已有数据库表,请使用 rename 方法:

php
use Illuminate\Support\Facades\Schema;

Schema::rename($from, $to);

要删除已有表,可使用 dropdropIfExists 方法:

php
Schema::drop('users');

Schema::dropIfExists('users');

重命名带外键的表

重命名表之前,应确认该表上的外键约束在迁移文件中都有显式名称,而不是让 Laravel 按约定自动命名。否则,外键约束名仍会引用旧表名。

创建列

Schema facade 的 table 方法可用于更新已有表。与 create 类似,table 接受两个参数:表名,以及接收 Illuminate\Database\Schema\Blueprint 实例的闭包,你可用它向表添加列:

php
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(主键)等价的列:

php
$table->bigIncrements('id');

bigInteger() {.collection-method}

bigInteger 方法会创建与 BIGINT 等价的列:

php
$table->bigInteger('votes');

binary() {.collection-method}

binary 方法会创建与 BLOB 等价的列:

php
$table->binary('photo');

使用 MySQL、MariaDB 或 SQL Server 时,可传入 lengthfixed 参数以创建与 VARBINARYBINARY 等价的列:

php
$table->binary('data', length: 16); // VARBINARY(16)

$table->binary('data', length: 16, fixed: true); // BINARY(16)

boolean() {.collection-method}

boolean 方法会创建与 BOOLEAN 等价的列:

php
$table->boolean('confirmed');

char() {.collection-method}

char 方法会创建指定长度的、与 CHAR 等价的列:

php
$table->char('name', length: 100);

dateTimeTz() {.collection-method}

dateTimeTz 方法会创建与 DATETIME(含时区)等价的列,并可指定小数秒精度:

php
$table->dateTimeTz('created_at', precision: 0);

dateTime() {.collection-method}

dateTime 方法会创建与 DATETIME 等价的列,并可指定小数秒精度:

php
$table->dateTime('created_at', precision: 0);

date() {.collection-method}

date 方法会创建与 DATE 等价的列:

php
$table->date('created_at');

decimal() {.collection-method}

decimal 方法会创建与 DECIMAL 等价的列,并指定精度(总位数)与小数位数:

php
$table->decimal('amount', total: 8, places: 2);

double() {.collection-method}

double 方法会创建与 DOUBLE 等价的列:

php
$table->double('amount');

enum() {.collection-method}

enum 方法会创建带有给定合法取值的、与 ENUM 等价的列:

php
$table->enum('difficulty', ['easy', 'hard']);

当然,你也可以使用 Enum::cases() 方法,而不必手动定义允许值数组:

php
use App\Enums\Difficulty;

$table->enum('difficulty', Difficulty::cases());

float() {.collection-method}

float 方法会创建指定精度的、与 FLOAT 等价的列:

php
$table->float('amount', precision: 53);

foreignId() {.collection-method}

foreignId 方法会创建与 UNSIGNED BIGINT 等价的列:

php
$table->foreignId('user_id');

foreignIdFor() {.collection-method}

foreignIdFor 方法会为给定模型类添加 {column}_id 等价列。列类型将根据模型主键类型为 UNSIGNED BIGINTCHAR(36)CHAR(26)

php
$table->foreignIdFor(User::class);

foreignUlid() {.collection-method}

foreignUlid 方法会创建与 ULID 等价的列:

php
$table->foreignUlid('user_id');

foreignUuid() {.collection-method}

foreignUuid 方法会创建与 UUID 等价的列:

php
$table->foreignUuid('user_id');

geography() {.collection-method}

geography 方法会创建带有给定空间类型与 SRID(空间参考系统标识符)的、与 GEOGRAPHY 等价的列:

php
$table->geography('coordinates', subtype: 'point', srid: 4326);

INFO

空间类型的支持取决于数据库驱动。请参阅所用数据库的文档。若应用使用 PostgreSQL,必须先安装 PostGIS 扩展,才能使用 geography 方法。

geometry() {.collection-method}

geometry 方法会创建带有给定空间类型与 SRID(空间参考系统标识符)的、与 GEOMETRY 等价的列:

php
$table->geometry('positions', subtype: 'point', srid: 0);

INFO

空间类型的支持取决于数据库驱动。请参阅所用数据库的文档。若应用使用 PostgreSQL,必须先安装 PostGIS 扩展,才能使用 geometry 方法。

id() {.collection-method}

id 方法是 bigIncrements 的别名。默认会创建名为 id 的列;若希望使用其他列名,可传入列名:

php
$table->id();

increments() {.collection-method}

increments 方法会创建自增的、与 UNSIGNED INTEGER 等价的列,并作为主键:

php
$table->increments('id');

integer() {.collection-method}

integer 方法会创建与 INTEGER 等价的列:

php
$table->integer('votes');

ipAddress() {.collection-method}

ipAddress 方法会创建与 VARCHAR 等价的列:

php
$table->ipAddress('visitor');

使用 PostgreSQL 时,会创建 INET 列。

json() {.collection-method}

json 方法会创建与 JSON 等价的列:

php
$table->json('options');

使用 SQLite 时,会创建 TEXT 列。

jsonb() {.collection-method}

jsonb 方法会创建与 JSONB 等价的列:

php
$table->jsonb('options');

使用 SQLite 时,会创建 TEXT 列。

longText() {.collection-method}

longText 方法会创建与 LONGTEXT 等价的列:

php
$table->longText('description');

使用 MySQL 或 MariaDB 时,可为列应用 binary 字符集,以创建与 LONGBLOB 等价的列:

php
$table->longText('data')->charset('binary'); // LONGBLOB

macAddress() {.collection-method}

macAddress 方法会创建用于存放 MAC 地址的列。某些数据库系统(如 PostgreSQL)对此类数据有专用列类型;其他系统则使用字符串等价列:

php
$table->macAddress('device');

mediumIncrements() {.collection-method}

mediumIncrements 方法会创建自增的、与 UNSIGNED MEDIUMINT 等价的列,并作为主键:

php
$table->mediumIncrements('id');

mediumInteger() {.collection-method}

mediumInteger 方法会创建与 MEDIUMINT 等价的列:

php
$table->mediumInteger('votes');

mediumText() {.collection-method}

mediumText 方法会创建与 MEDIUMTEXT 等价的列:

php
$table->mediumText('description');

使用 MySQL 或 MariaDB 时,可为列应用 binary 字符集,以创建与 MEDIUMBLOB 等价的列:

php
$table->mediumText('data')->charset('binary'); // MEDIUMBLOB

morphs() {.collection-method}

morphs 方法是便捷方法,会添加 {column}_id 等价列,以及 {column}_typeVARCHAR 等价列。{column}_id 的列类型将根据模型主键类型为 UNSIGNED BIGINTCHAR(36)CHAR(26)

该方法用于定义多态 Eloquent 关联 所需的列。在下面的例子中,会创建 taggable_idtaggable_type 列:

php
$table->morphs('taggable');

nullableMorphs() {.collection-method}

该方法与 morphs 类似;不过所创建的列可为「可空」:

php
$table->nullableMorphs('taggable');

nullableUlidMorphs() {.collection-method}

该方法与 ulidMorphs 类似;不过所创建的列可为「可空」:

php
$table->nullableUlidMorphs('taggable');

nullableUuidMorphs() {.collection-method}

该方法与 uuidMorphs 类似;不过所创建的列可为「可空」:

php
$table->nullableUuidMorphs('taggable');

rememberToken() {.collection-method}

rememberToken 方法会创建可空的、与 VARCHAR(100) 等价的列,用于存储当前的「记住我」认证令牌

php
$table->rememberToken();

set() {.collection-method}

set 方法会创建带有给定合法取值列表的、与 SET 等价的列:

php
$table->set('flavors', ['strawberry', 'vanilla']);

smallIncrements() {.collection-method}

smallIncrements 方法会创建自增的、与 UNSIGNED SMALLINT 等价的列,并作为主键:

php
$table->smallIncrements('id');

smallInteger() {.collection-method}

smallInteger 方法会创建与 SMALLINT 等价的列:

php
$table->smallInteger('votes');

softDeletesTz() {.collection-method}

softDeletesTz 方法会添加可空的 deleted_at、与 TIMESTAMP(含时区)等价的列,并可指定小数秒精度。该列用于存储 Eloquent「软删除」功能所需的 deleted_at 时间戳:

php
$table->softDeletesTz('deleted_at', precision: 0);

softDeletes() {.collection-method}

softDeletes 方法会添加可空的 deleted_at、与 TIMESTAMP 等价的列,并可指定小数秒精度。该列用于存储 Eloquent「软删除」功能所需的 deleted_at 时间戳:

php
$table->softDeletes('deleted_at', precision: 0);

string() {.collection-method}

string 方法会创建指定长度的、与 VARCHAR 等价的列:

php
$table->string('name', length: 100);

text() {.collection-method}

text 方法会创建与 TEXT 等价的列:

php
$table->text('description');

使用 MySQL 或 MariaDB 时,可为列应用 binary 字符集,以创建与 BLOB 等价的列:

php
$table->text('data')->charset('binary'); // BLOB

timeTz() {.collection-method}

timeTz 方法会创建与 TIME(含时区)等价的列,并可指定小数秒精度:

php
$table->timeTz('sunrise', precision: 0);

time() {.collection-method}

time 方法会创建与 TIME 等价的列,并可指定小数秒精度:

php
$table->time('sunrise', precision: 0);

timestampTz() {.collection-method}

timestampTz 方法会创建与 TIMESTAMP(含时区)等价的列,并可指定小数秒精度:

php
$table->timestampTz('added_at', precision: 0);

timestamp() {.collection-method}

timestamp 方法会创建与 TIMESTAMP 等价的列,并可指定小数秒精度:

php
$table->timestamp('added_at', precision: 0);

timestampsTz() {.collection-method}

timestampsTz 方法会创建 created_atupdated_at、与 TIMESTAMP(含时区)等价的列,并可指定小数秒精度:

php
$table->timestampsTz(precision: 0);

timestamps() {.collection-method}

timestamps 方法会创建 created_atupdated_at、与 TIMESTAMP 等价的列,并可指定小数秒精度:

php
$table->timestamps(precision: 0);

tinyIncrements() {.collection-method}

tinyIncrements 方法会创建自增的、与 UNSIGNED TINYINT 等价的列,并作为主键:

php
$table->tinyIncrements('id');

tinyInteger() {.collection-method}

tinyInteger 方法会创建与 TINYINT 等价的列:

php
$table->tinyInteger('votes');

tinyText() {.collection-method}

tinyText 方法会创建与 TINYTEXT 等价的列:

php
$table->tinyText('notes');

使用 MySQL 或 MariaDB 时,可为列应用 binary 字符集,以创建与 TINYBLOB 等价的列:

php
$table->tinyText('data')->charset('binary'); // TINYBLOB

unsignedBigInteger() {.collection-method}

unsignedBigInteger 方法会创建与 UNSIGNED BIGINT 等价的列:

php
$table->unsignedBigInteger('votes');

unsignedInteger() {.collection-method}

unsignedInteger 方法会创建与 UNSIGNED INTEGER 等价的列:

php
$table->unsignedInteger('votes');

unsignedMediumInteger() {.collection-method}

unsignedMediumInteger 方法会创建与 UNSIGNED MEDIUMINT 等价的列:

php
$table->unsignedMediumInteger('votes');

unsignedSmallInteger() {.collection-method}

unsignedSmallInteger 方法会创建与 UNSIGNED SMALLINT 等价的列:

php
$table->unsignedSmallInteger('votes');

unsignedTinyInteger() {.collection-method}

unsignedTinyInteger 方法会创建与 UNSIGNED TINYINT 等价的列:

php
$table->unsignedTinyInteger('votes');

ulidMorphs() {.collection-method}

ulidMorphs 方法是便捷方法,会添加 {column}_idCHAR(26) 等价列,以及 {column}_typeVARCHAR 等价列。

该方法用于定义使用 ULID 标识符的多态 Eloquent 关联 所需的列。在下面的例子中,会创建 taggable_idtaggable_type 列:

php
$table->ulidMorphs('taggable');

uuidMorphs() {.collection-method}

uuidMorphs 方法是便捷方法,会添加 {column}_idCHAR(36) 等价列,以及 {column}_typeVARCHAR 等价列。

该方法用于定义使用 UUID 标识符的多态 Eloquent 关联 所需的列。在下面的例子中,会创建 taggable_idtaggable_type 列:

php
$table->uuidMorphs('taggable');

ulid() {.collection-method}

ulid 方法会创建与 ULID 等价的列:

php
$table->ulid('id');

uuid() {.collection-method}

uuid 方法会创建与 UUID 等价的列:

php
$table->uuid('id');

vector() {.collection-method}

vector 方法会创建与 vector 等价的列:

php
$table->vector('embedding', dimensions: 100);

使用 PostgreSQL 时,必须先加载 pgvector 扩展,才能创建 vector 列:

php
Schema::ensureVectorExtensionExists();

year() {.collection-method}

year 方法会创建与 YEAR 等价的列:

php
$table->year('birth_year');

列修饰符

除了上面列出的列类型外,向数据库表添加列时还可使用若干列「修饰符」。例如,要让列「可空」,可使用 nullable 方法:

php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

Schema::table('users', function (Blueprint $table) {
    $table->string('email')->nullable();
});

下表列出了全部可用的列修饰符。本列表不包含索引修饰符

修饰符说明
->after('column')将该列放在另一列「之后」(MariaDB / MySQL)。
->autoIncrement()INTEGER 列设为自增(主键)。
->charset('utf8mb4')为列指定字符集(MariaDB / MySQL)。
->collation('utf8mb4_unicode_ci')为列指定排序规则。
->comment('my comment')为列添加注释(MariaDB / MySQL / PostgreSQL)。
->default($value)为列指定「默认」值。
->first()将该列放在表的「最前」(MariaDB / MySQL)。
->from($integer)设置自增字段的起始值(MariaDB / MySQL / PostgreSQL)。
->instant()使用即时操作添加或修改列(MySQL)。
->invisible()使列对 SELECT * 查询「不可见」(MariaDB / MySQL)。
->lock($mode)为列操作指定锁模式(MySQL)。
->nullable($value = true)允许向该列插入 NULL 值。
->storedAs($expression)创建存储型生成列(MariaDB / MySQL / PostgreSQL / SQLite)。
->unsigned()INTEGER 列设为 UNSIGNED(MariaDB / MySQL)。
->useCurrent()TIMESTAMP 列默认值设为 CURRENT_TIMESTAMP
->useCurrentOnUpdate()记录更新时将 TIMESTAMP 列设为 CURRENT_TIMESTAMP(MariaDB / MySQL)。
->virtualAs($expression)创建虚拟生成列(MariaDB / MySQL / SQLite)。
->generatedAs($expression)创建带指定序列选项的 identity 列(PostgreSQL)。
->always()定义 identity 列上序列值相对于输入值的优先级(PostgreSQL)。

默认表达式

default 修饰符可接受一个值,或一个 Illuminate\Database\Query\Expression 实例。使用 Expression 实例可阻止 Laravel 给值加引号,从而允许使用特定于数据库的函数。在需要为 JSON 列指定默认值时,这一点尤其有用:

php
<?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 中已有列之后添加列:

php
$table->after('password', function (Blueprint $table) {
    $table->string('address_line1');
    $table->string('address_line2');
    $table->string('city');
});

即时列操作

使用 MySQL 时,可在列定义上链式调用 instant 修饰符,表示应使用 MySQL 的「即时」算法添加或修改该列。该算法可让某些 schema 变更无需完整重建表,无论表多大几乎都能瞬间完成:

php
$table->string('name')->nullable()->instant();

即时添加列只能把列追加到表末尾,因此 instant 不能与 afterfirst 修饰符组合使用。此外,该算法并不支持所有列类型或操作。若请求的操作不兼容,MySQL 会报错。

请参阅 MySQL 文档,以确认哪些操作与即时列修改兼容。

DDL 锁

使用 MySQL 时,可在列、索引或外键定义上链式调用 lock 修饰符,以控制 schema 操作期间的表锁。MySQL 支持多种锁模式:none 允许并发读写,shared 允许并发读但阻塞写,exclusive 阻塞所有并发访问,default 则由 MySQL 选择最合适的模式:

php
$table->string('name')->lock('none');

$table->index('email')->lock('shared');

若请求的锁模式与操作不兼容,MySQL 会报错。lock 修饰符可与 instant 组合,以进一步优化 schema 变更:

php
$table->string('name')->instant()->lock('none');

修改列

change 方法允许你修改已有列的类型与属性。例如,你可能希望增大 string 列的长度。下面将 name 列从 25 增加到 50:只需定义列的新状态,然后调用 change 方法:

php
Schema::table('users', function (Blueprint $table) {
    $table->string('name', 50)->change();
});

修改列时,必须显式包含你希望保留在列定义上的全部修饰符——任何缺失的属性都会被丢弃。例如,要保留 unsigneddefaultcomment 属性,修改列时必须显式调用每个修饰符:

php
Schema::table('users', function (Blueprint $table) {
    $table->integer('votes')->unsigned()->default(1)->comment('my comment')->change();
});

change 方法不会更改列上的索引。因此,修改列时可用索引修饰符显式添加或删除索引:

php
// Add an index...
$table->bigIncrements('id')->primary()->change();

// Drop an index...
$table->char('postal_code', 10)->unique(false)->change();

重命名列

要重命名列,可使用 schema 构建器提供的 renameColumn 方法:

php
Schema::table('users', function (Blueprint $table) {
    $table->renameColumn('from', 'to');
});

删除列

要删除列,可在 schema 构建器上使用 dropColumn 方法:

php
Schema::table('users', function (Blueprint $table) {
    $table->dropColumn('votes');
});

dropColumn 传入列名数组,可一次删除表中的多列:

php
Schema::table('users', function (Blueprint $table) {
    $table->dropColumn(['votes', 'avatar', 'location']);
});

可用命令别名

Laravel 提供了若干与删除常见列类型相关的便捷方法。各方法说明见下表:

命令说明
$table->dropMorphs('morphable');Drop the morphable_id and morphable_type columns.
$table->dropRememberToken();删除 remember_token 列。
$table->dropSoftDeletes();删除 deleted_at 列。
$table->dropSoftDeletesTz();dropSoftDeletes() 方法的别名。
$table->dropTimestamps();删除 created_atupdated_at 列。
$table->dropTimestampsTz();dropTimestamps() 方法的别名。

索引

创建索引

Laravel schema 构建器支持多种索引类型。下面的例子创建新的 email 列,并指定其值应唯一。要创建索引,可在列定义上链式调用 unique 方法:

php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

Schema::table('users', function (Blueprint $table) {
    $table->string('email')->unique();
});

或者,你也可以在定义列之后再创建索引。为此,应在 schema 构建器 blueprint 上调用 unique 方法。该方法接受应建立唯一索引的列名:

php
$table->unique('email');

你甚至可以向索引方法传入列名数组,以创建复合(组合)索引:

php
$table->index(['account_id', 'created_at']);

创建索引时,Laravel 会根据表名、列名与索引类型自动生成索引名,但你也可以向方法传入第二个参数以自行指定索引名:

php
$table->unique('email', 'unique_email');

可用索引类型

Laravel 的 schema 构建器 blueprint 类为 Laravel 支持的每种索引类型都提供了创建方法。每个索引方法都接受可选的第二个参数以指定索引名;若省略,名称将由表名、用于索引的列名以及索引类型推导得出。可用索引方法见下表:

命令说明
$table->primary('id');添加主键。
$table->primary(['id', 'parent_id']);添加复合主键。
$table->unique('email');添加唯一索引。
$table->index('state');添加索引。
$table->fullText('body');添加全文索引(MariaDB / MySQL / PostgreSQL)。
$table->fullText('body')->language('english');添加指定语言的全文索引(PostgreSQL)。
$table->spatialIndex('location');添加空间索引(SQLite 除外)。

在线创建索引

默认情况下,在大表上创建索引会锁表,并在构建索引期间阻塞读或写。使用 PostgreSQL 或 SQL Server 时,可在索引定义上链式调用 online 方法,在不锁表的情况下创建索引,从而让应用在创建索引期间继续读写数据:

php
$table->string('email')->unique()->online();

使用 PostgreSQL 时,这会向建索引语句添加 CONCURRENTLY 选项;使用 SQL Server 时,会添加 WITH (online = on) 选项。

重命名索引

要重命名索引,可使用 schema 构建器 blueprint 提供的 renameIndex 方法。该方法的第一个参数为当前索引名,第二个参数为期望的新名称:

php
$table->renameIndex('from', 'to')

删除索引

要删除索引,必须指定索引名。默认情况下,Laravel 会根据表名、被索引列名与索引类型自动分配索引名。示例如下:

命令说明
$table->dropPrimary('users_id_primary');从「users」表删除主键。
$table->dropUnique('users_email_unique');从「users」表删除唯一索引。
$table->dropIndex('geo_state_index');从「geo」表删除普通索引。
$table->dropFullText('posts_body_fulltext');从「posts」表删除全文索引。
$table->dropSpatialIndex('geo_location_spatialindex');从「geo」表删除空间索引(SQLite 除外)。

若向删除索引的方法传入列名数组,将根据表名、列名与索引类型生成约定式索引名:

php
Schema::table('geo', function (Blueprint $table) {
    $table->dropIndex(['state']); // Drops index 'geo_state_index'
});

外键约束

Laravel 还支持创建外键约束,用于在数据库层面强制引用完整性。例如,我们在 posts 表上定义引用 usersid 列的 user_id 列:

php
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 创建列时,上面的例子可改写为:

php
Schema::table('posts', function (Blueprint $table) {
    $table->foreignId('user_id')->constrained();
});

foreignId 方法会创建与 UNSIGNED BIGINT 等价的列,而 constrained 方法会按约定推断被引用的表与列。若表名不符合 Laravel 约定,可手动传给 constrained。另外,也可一并指定生成索引应使用的名称:

php
Schema::table('posts', function (Blueprint $table) {
    $table->foreignId('user_id')->constrained(
        table: 'users', indexName: 'posts_user_id'
    );
});

你还可以为约束的「on delete」与「on update」属性指定期望的行为:

php
$table->foreignId('user_id')
    ->constrained()
    ->onUpdate('cascade')
    ->onDelete('cascade');

这些动作也提供了另一种更富表现力的写法:

方法说明
$table->cascadeOnUpdate();更新应级联。
$table->restrictOnUpdate();更新应受限。
$table->nullOnUpdate();更新时应将外键值设为 null。
$table->noActionOnUpdate();更新时不采取动作。
$table->cascadeOnDelete();删除应级联。
$table->restrictOnDelete();删除应受限。
$table->nullOnDelete();删除时应将外键值设为 null。
$table->noActionOnDelete();若存在子记录则阻止删除。

任何额外的列修饰符都必须在 constrained 方法之前调用:

php
$table->foreignId('user_id')
    ->nullable()
    ->constrained();

删除外键

要删除外键,可使用 dropForeign 方法,并将要删除的外键约束名作为参数传入。外键约束与索引使用相同的命名约定。换句话说,外键约束名由表名与约束中的列名组成,并带有 _foreign 后缀:

php
$table->dropForeign('posts_user_id_foreign');

或者,你也可以向 dropForeign 传入包含外键列名的数组。该数组会按 Laravel 的约束命名约定转换为外键约束名:

php
$table->dropForeign(['user_id']);

开关外键约束

你可以在迁移中使用以下方法启用或禁用外键约束:

php
Schema::enableForeignKeyConstraints();

Schema::disableForeignKeyConstraints();

Schema::withoutForeignKeyConstraints(function () {
    // Constraints disabled within this closure...
});

WARNING

SQLite 默认禁用外键约束。使用 SQLite 时,请确保在尝试于迁移中创建外键之前,已在数据库配置中启用外键支持

事件

为方便起见,每次迁移操作都会派发一个事件。下列事件均继承自基类 Illuminate\Database\Events\MigrationEvent

说明
Illuminate\Database\Events\MigrationsStarted一批迁移即将执行。
Illuminate\Database\Events\MigrationsEndedA batch of migrations has finished executing.
Illuminate\Database\Events\MigrationStarted单个迁移即将执行。
Illuminate\Database\Events\MigrationEndedA single migration has finished executing.
Illuminate\Database\Events\NoPendingMigrations迁移命令未发现待执行迁移。
Illuminate\Database\Events\SchemaDumpedA database schema dump has completed.
Illuminate\Database\Events\SchemaLoaded已加载既有数据库 schema 导出。