数据库迁移

未匹配的标注
本文档最新版为 11.x,旧版本可能放弃维护,推荐阅读最新版!

数据库:迁移

简介

迁移就像数据库的版本控制,允许团队定义并共享应用程序的数据库结构定义。
如果你曾经在拉取源码后,需要告诉队友手动在本地数据库中添加一个列,那么你就遇到了迁移要解决的问题。

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

生成迁移

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

php artisan make:migration create_flights_table

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

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

[!注意]
迁移模板(migration stubs)可以通过 stub 发布 进行自定义。

合并迁移

随着应用程序的发展,你可能会随着时间积累越来越多的迁移。
这可能导致 database/migrations 目录膨胀,可能包含上百个迁移文件。

如果需要,你可以将这些迁移“合并”为一个单独的 SQL 文件。
要开始操作,可以执行 schema:dump 命令:

php artisan schema:dump

# 导出当前数据库结构,并修剪所有已有迁移...
php artisan schema:dump --prune

执行该命令时,Laravel 会将一个“schema”文件写入应用的 database/schema 目录。
该 schema 文件的名称将对应于数据库连接名称。

现在,当你尝试迁移数据库且没有其他迁移已执行时,
Laravel 会首先执行你正在使用的数据库连接的 schema 文件中的 SQL 语句。
在执行完 schema 文件中的 SQL 语句后,Laravel 会执行任何未包含在 schema dump 中的剩余迁移。

如果你的应用测试使用的数据库连接与本地开发时通常使用的不同,
你应确保已经使用该测试数据库连接导出过 schema 文件,这样测试才能构建数据库。
你可能希望在导出本地开发使用的数据库连接后执行:

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

你应该将数据库 schema 文件提交到版本控制,这样团队中新的开发者就可以快速创建应用程序的初始数据库结构。

[!警告]
迁移合并(Migration Squashing)仅适用于 MariaDB、MySQL、PostgreSQL 和 SQLite 数据库,并使用数据库的命令行客户端。

迁移结构

一个迁移类包含两个方法:updown

  • up 方法用于向数据库添加新的表、列或索引
  • down 方法应当撤销 up 方法执行的操作

在这两个方法中,你可以使用 Laravel 的 schema 构建器(Schema Builder)以可读的方式创建和修改表。
要了解 schema 构建器的所有可用方法,请查看其文档

例如,下面的迁移创建了一个 flights 表:

<?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');
    }
};

设置迁移连接(Setting the Migration Connection)

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

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

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

跳过迁移

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

use App\Models\Flights;
use Laravel\Pennant\Feature;

/**
 * 判断此迁移是否应该执行
 */
public function shouldRun(): bool
{
    return Feature::active(Flights::class);
}

运行迁移

要运行所有待执行的迁移,请执行 migrate Artisan 命令:

php artisan migrate

如果你想查看哪些迁移已经执行过,可以使用 migrate:status Artisan 命令:

php artisan migrate:status

如果你想查看迁移将要执行的 SQL 语句,但不实际运行迁移,可以为 migrate 命令提供 --pretend 标志:

php artisan migrate --pretend

隔离迁移执行

如果你在多个服务器上部署应用,并在部署过程中运行迁移,
你很可能不希望两个服务器同时尝试迁移数据库。

为避免这种情况,你可以在执行 migrate 命令时使用 --isolated 选项。

当提供 isolated 选项时,Laravel 会使用应用的缓存驱动获取一个原子锁(atomic lock),然后再尝试运行迁移。
在锁被持有期间,其他尝试运行 migrate 命令的操作不会执行;
不过命令仍会以成功退出状态码结束:

php artisan migrate --isolated

[!警告]
使用此功能时,你的应用必须使用 memcachedredisdynamodbdatabasefilearray 作为默认缓存驱动。
并且,所有服务器必须与同一个中央缓存服务器通信。

在生产环境强制运行迁移

某些迁移操作是破坏性的(destructive),这意味着它们可能导致数据丢失。
为了保护你在生产数据库上执行这些命令,执行前会提示确认。
如果想在不提示确认的情况下强制执行命令,可以使用 --force 标志:

php artisan migrate --force

回滚迁移

要回滚最近的一次迁移操作,可以使用 rollback Artisan 命令。
此命令会回滚最后一个“批次(batch)”的迁移,这个批次可能包含多个迁移文件:

php artisan migrate:rollback

你可以通过为 rollback 命令提供 --step 选项来回滚有限数量的迁移。
例如,以下命令将回滚最近五个迁移:

php artisan migrate:rollback --step=5

你也可以通过提供 --batch 选项回滚特定批次的迁移,其中 batch 选项对应于应用 migrations 数据表中的批次值。
例如,以下命令将回滚第 3 批次的所有迁移:

php artisan migrate:rollback --batch=3

如果你想查看回滚迁移将执行的 SQL 语句,但不实际执行,可以为 migrate:rollback 命令提供 --pretend 标志:

php artisan migrate:rollback --pretend

migrate:reset 命令会回滚应用的所有迁移:

php artisan migrate:reset

使用单条命令回滚并迁移

migrate:refresh 命令会回滚所有迁移,然后再执行 migrate 命令。
此命令实际上会重新创建整个数据库:

php artisan migrate:refresh

# 刷新数据库并运行所有数据库填充(seed)...
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=admin

[!警告]
migrate:fresh 命令会删除数据库中所有表,无论表前缀是什么。
在开发共享数据库的应用时应谨慎使用此命令。

创建表

要创建一个新的数据库表,使用 Schema facade 的 create 方法。
create 方法接受两个参数:

  • 第一个参数是表名
  • 第二个参数是一个闭包(closure),接收一个 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 builder)的任意 列方法 来定义表的列。

判断表 / 列是否存在

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

if (Schema::hasTable('users')) {
    // "users" 表存在...
}

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

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

数据库连接和表选项

如果你希望在非应用默认数据库连接上执行模式操作,可使用 connection 方法:

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

此外,还可以使用其他一些属性和方法来定义表的其他方面。

  • engine 属性可用于在 MariaDB 或 MySQL 中指定表的存储引擎:
Schema::create('users', function (Blueprint $table) {
    $table->engine('InnoDB');

    // ...
});

charsetcollation 属性可用于在 MariaDB 或 MySQL 中指定创建表的字符集和排序规则:

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 方法接受两个参数:表名以及一个闭包(closure),闭包接收一个 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);

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

Schema::drop('users');

Schema::dropIfExists('users');

带外键的表重命名

在重命名表之前,你应该确认表上的外键约束在迁移文件中有明确的名称,而不是让 Laravel 使用约定生成的名称。
否则,外键约束名称将引用旧的表名。

创建列

Schema facade 的 table 方法可用于更新现有表。
create 方法一样,table 方法接受两个参数:表名以及一个闭包(closure),闭包接收一个 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 提供了多种方法,对应于你可以向数据库表中添加的不同列类型。
下面的表格列出了所有可用的方法:

布尔类型(Boolean Types)

字符串与文本类型(String & Text Types)

数值类型(Numeric Types)

日期与时间类型(Date & Time Types)

二进制类型(Binary Types)

对象与 JSON 类型(Object & Json Types)

UUID 与 ULID 类型(UUID & ULID Types)

空间类型(Spatial Types)

关系类型(Relationship Types)

特殊类型(Specialty Types)

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 时,你可以传入 lengthfixed 参数来创建 VARBINARYBINARY 等效列:

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

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

[!注意]
空间类型支持取决于你的数据库驱动。如果你的应用使用 PostgreSQL 数据库,必须先安装 PostGIS 扩展,才能使用 geography 方法。

geometry() {.collection-method}

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

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

[!注意]
空间类型支持取决于你的数据库驱动。请参考数据库文档。如果你的应用使用 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 BIGINTCHAR(36)CHAR(26)

此方法用于定义多态 Eloquent 关系 所需的列。例如,以下示例会创建 taggable_idtaggable_type 列:

$table->morphs('taggable');

nullableMorphs() {.collection-method}

此方法与 morphs 方法类似,但创建的列将允许为空(nullable):

$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_atupdated_at 两个带时区的 TIMESTAMP 等效列,可指定小数秒精度:

$table->timestampsTz(precision: 0);

timestamps() {.collection-method}

timestamps 方法创建 created_atupdated_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_idtaggable_type 列:

$table->ulidMorphs('taggable');

uuidMorphs() {.collection-method}

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

此方法旨在定义使用 UUID 标识符的多态 Eloquent 关联 所需的列时使用。 在以下示例中,将创建 taggable_idtaggable_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();
});

下表包含所有可用的列修饰符。 此列表不包括 索引修饰符

修饰符 描述
->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)。
->invisible() 使列对 SELECT * 查询「不可见」(MariaDB / 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

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();
        });
    }
};

[!警告]
对默认表达式的支持取决于您的数据库驱动、数据库版本和字段类型。 请参阅您的数据库文档。

列顺序

使用 MariaDB 或 MySQL 数据库时,可以使用 after 方法在模式中的现有列之后添加列:

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

修改列

change 方法允许您修改现有列的类型和属性。 例如,您可能希望增加 string 列的大小。 要查看 change 方法的实际效果,让我们将 name 列的大小从 25 增加到 50。 为此,我们只需定义列的新状态,然后调用 change 方法:

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

修改列时,必须在列定义中显式包含您想要保留的所有修饰符 - 任何缺失的属性都将被移除。 例如,要保留 unsigneddefaultcomment 属性,在更改列时必须显式调用每个修饰符:

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

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

// 添加索引...
$table->bigIncrements('id')->primary()->change();

// 删除索引...
$table->char('postal_code', 10)->unique(false)->change();

重命名列

要重命名列,您可以使用模式构建器提供的 renameColumn 方法:

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

删除列

要删除列,您可以在模式构建器上使用 dropColumn 方法:

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

您可以通过将列名数组传递给 dropColumn 方法来从表中删除多个列:

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 模式构建器支持多种类型的索引。 以下示例创建了一个新的 email 列并指定其值应该是唯一的。 要创建索引,我们可以将 unique 方法链式调用到列定义上:

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

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

或者,您也可以在定义列之后创建索引。 为此,您应该在模式构建器蓝图对象上调用 unique 方法。 此方法接受应该创建唯一索引的列名:

$table->unique('email');

您甚至可以将列数组传递给索引方法以创建复合索引:

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

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

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

可用的索引类型

Laravel 的模式构建器蓝图类提供了用于创建 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 除外)。

重命名索引

要重命名索引,可以使用 Schema 构建器 提供的 renameIndex 方法。
此方法的第一个参数是当前索引的名称,第二个参数是期望的新名称

$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 除外)。

如果你在删除索引的方法里传递的是 列名数组,Laravel 会根据表名、列名和索引类型自动推断出索引名:

Schema::table('geo', function (Blueprint $table) {
    $table->dropIndex(['state']); // 删除索引 'geo_state_index'
});

外键约束

Laravel 也支持创建 外键约束,它可以在数据库层面强制 参照完整性
例如,定义 posts 表中的 user_id 列,让它引用 users 表中的 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');
});

由于这种语法(前面的 $table->unsignedBigInteger('user_id') ...)比较冗长,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');

同时,Laravel 也为这些操作提供了一种更具表现力的替代语法:

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

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

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

删除外键

要删除一个外键,可以使用 dropForeign 方法,并将要删除的外键约束的名称作为参数传递。
外键约束的命名规则与索引相同。换句话说,外键约束的名称是基于表名约束中涉及的列名,然后加上一个 _foreign 后缀:

$table->dropForeign('posts_user_id_foreign');

另外,你也可以传递一个包含外键列名的数组给 dropForeign 方法。该数组会根据 Laravel 的约束命名约定被转换为外键约束名称:

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

切换外键约束

你可以在迁移(migrations)中通过以下方法启用或禁用外键约束:

Schema::enableForeignKeyConstraints();

Schema::disableForeignKeyConstraints();

Schema::withoutForeignKeyConstraints(function () {
    // 在这个闭包中,外键约束被禁用...
});

[!警告]
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)导出。

本文章首发在 LearnKu.com 网站上。

本译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。

原文地址:https://learnku.com/docs/laravel/12.x/mi...

译文地址:https://learnku.com/docs/laravel/12.x/mi...

上一篇 下一篇
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
贡献者:2
讨论数量: 9
发起讨论 只看当前版本


kiyoma
多人开发的时候,migration 不会有问题吗?
5 个点赞 | 23 个回复 | 问答 | 课程版本 5.5
Neox
table 的 comment 怎么创建?
4 个点赞 | 11 个回复 | 问答 | 课程版本 5.5
15072306389
觉得文档内容太多太复杂的可以这个
2 个点赞 | 0 个回复 | 分享 | 课程版本 5.4
osang
数据库迁移如何解决外键依赖问题?
1 个点赞 | 2 个回复 | 问答 | 课程版本 5.8
chenvle
修改字段 运行 php artisan migrate 报错
0 个点赞 | 5 个回复 | 问答 | 课程版本 5.5
xgrit
项目配置完数据库执行 PHP artisan magerate 报错
0 个点赞 | 4 个回复 | 问答 | 课程版本 5.8
MoYuEr
在数据库迁移时被创建了意料之外的表!
0 个点赞 | 3 个回复 | 分享 | 课程版本 8.x
dana
String data, right truncated: 1406 Data too long for column 'migration'
0 个点赞 | 2 个回复 | 问答 | 课程版本 8.x
caiyaonan
迁移文件的字段是不是没有 zerofille 修饰呢
0 个点赞 | 1 个回复 | 问答 | 课程版本 5.4