Skip to content

数据库:迁移

简介

迁移就像数据库的版本控制,允许你的团队定义和共享应用程序的数据库结构定义。如果你曾经不得不告诉团队成员在从源代码控制中拉取更改后手动向其本地数据库结构添加列,那么你就遇到了数据库迁移所解决的问题。

Laravel Schema facade 提供了与数据库无关的支持,用于在 Laravel 支持的所有数据库系统中创建和操作表。通常,迁移将使用此 facade 来创建和修改数据库表和列。

生成迁移

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

shell
php artisan make:migration create_flights_table

Laravel 将使用迁移的名称来尝试猜测表名以及迁移是否将创建新表。如果 Laravel 能够从迁移名称确定表名,Laravel 将使用指定的表预填充生成的迁移文件。否则,你可以在迁移文件中手动指定表。

如果你想为生成的迁移指定自定义路径,可以在执行 make:migration 命令时使用 --path 选项。给定的路径应相对于应用程序的基础路径。

NOTE

迁移存根可以使用存根发布进行自定义。

压缩迁移

随着应用程序的构建,你可能会随着时间的推移积累越来越多的迁移。这可能导致你的 database/migrations 目录膨胀,包含数百个迁移。如果你愿意,可以将迁移「压缩」为单个 SQL 文件。要开始,请执行 schema:dump 命令:

shell
php artisan schema:dump

# 转储当前数据库结构并删除所有现有迁移...
php artisan schema:dump --prune

当你执行此命令时,Laravel 会将一个「schema」文件写入应用程序的 database/schema 目录。schema 文件的名称将对应于数据库连接。现在,当你尝试迁移数据库且没有其他迁移被执行时,Laravel 将首先执行你正在使用的数据库连接的 schema 文件中的 SQL 语句。执行 schema 文件的 SQL 语句后,Laravel 将执行不属于 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
{
    /**
     * 运行迁移。
     */
    public function up(): void
    {
        Schema::create('flights', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->string('airline');
            $table->timestamps();
        });
    }

    /**
     * 反转迁移。
     */
    public function down(): void
    {
        Schema::drop('flights');
    }
};

设置迁移连接

如果你的迁移将与应用程序默认数据库连接以外的数据库连接交互,你应该设置迁移的 $connection 属性:

php
/**
 * 迁移应使用的数据库连接。
 *
 * @var string
 */
protected $connection = 'pgsql';

/**
 * 运行迁移。
 */
public function up(): void
{
    // ...
}

跳过迁移

有时迁移可能是为了支持尚未激活的功能,你不希望它立即运行。在这种情况下,你可以在迁移上定义一个 shouldRun 方法。如果 shouldRun 方法返回 false,迁移将被跳过:

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

/**
 * 确定此迁移是否应该运行。
 */
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

# 刷新数据库并运行所有数据库填充...
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')) {
    // "users" 表存在...
}

if (Schema::hasColumn('users', 'email')) {
    // "users" 表存在且有 "email" 列...
}

if (Schema::hasIndex('users', ['email'], 'unique')) {
    // "users" 表存在且 "email" 列上有唯一索引...
}

数据库连接和表选项

如果你想在不是应用程序默认连接的数据库连接上执行 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 构建器蓝图提供了多种方法,对应于你可以添加到数据库表中的不同类型的列。每个可用的方法都列在下表中:

布尔类型

字符串和文本类型

数字类型

日期和时间类型

二进制类型

对象和 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 方法创建一个 GEOGRAPHY 等效列,具有给定的空间类型和 SRID(空间参考系统标识符):

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

NOTE

空间类型的支持取决于你的数据库驱动。请参阅你的数据库文档。如果你的应用程序使用 PostgreSQL 数据库,则必须在使用 geography 方法之前安装 PostGIS 扩展。

geometry() {.collection-method}

geometry 方法创建一个 GEOMETRY 等效列,具有给定的空间类型和 SRID(空间参考系统标识符):

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

NOTE

空间类型的支持取决于你的数据库驱动。请参阅你的数据库文档。如果你的应用程序使用 PostgreSQL 数据库,则必须在使用 geometry 方法之前安装 PostGIS 扩展。

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}_type VARCHAR 等效列。{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}_id CHAR(26) 等效列和 {column}_type VARCHAR 等效列。

此方法旨在用于定义使用 ULID 标识符的多态 Eloquent 关联所需的列。在以下示例中,将创建 taggable_idtaggable_type 列:

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

uuidMorphs() {.collection-method}

uuidMorphs 方法是一个便捷方法,用于添加 {column}_id CHAR(36) 等效列和 {column}_type VARCHAR 等效列。

此方法旨在用于定义使用 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 时,必须在创建 vector 列之前加载 pgvector 扩展:

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)使用指定的序列选项创建标识列(PostgreSQL)。
->always()定义序列值优先于标识列输入的优先级(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
{
    /**
     * 运行迁移。
     */
    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 的「instant」算法添加或修改列。此算法允许在不进行完整表重建的情况下执行某些 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 列的大小。要查看 change 方法的实际效果,让我们将 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
// 添加索引...
$table->bigIncrements('id')->primary()->change();

// 删除索引...
$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');删除 morphable_idmorphable_type 列。
$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 构建器蓝图上调用 unique 方法。此方法接受应接收唯一索引的列名:

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

你甚至可以向索引方法传递列数组以创建复合索引:

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

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

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

可用的索引类型

Laravel 的 schema 构建器蓝图类提供了用于创建 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 构建器蓝图提供的 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']); // 删除索引 'geo_state_index'
});

外键约束

Laravel 还提供了创建外键约束的支持,用于在数据库级别强制引用完整性。例如,让我们在 posts 表上定义一个引用 users 表的 id 列的 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 () {
    // 在此闭包中禁用约束...
});

WARNING

SQLite 默认禁用外键约束。使用 SQLite 时,确保在数据库配置中启用外键支持,然后再尝试在迁移中创建它们。

事件

为方便起见,每个迁移操作都会调度一个事件。以下所有事件都扩展了基础 Illuminate\Database\Events\MigrationEvent 类:

描述
Illuminate\Database\Events\MigrationsStarted一批迁移即将被执行。
Illuminate\Database\Events\MigrationsEnded一批迁移已完成执行。
Illuminate\Database\Events\MigrationStarted单个迁移即将被执行。
Illuminate\Database\Events\MigrationEnded单个迁移已完成执行。
Illuminate\Database\Events\NoPendingMigrations迁移命令未找到待处理的迁移。
Illuminate\Database\Events\SchemaDumped数据库 schema 转储已完成。
Illuminate\Database\Events\SchemaLoaded已加载现有的数据库 schema 转储。