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
{
    // ...
}

运行迁移

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

shell
php artisan migrate

若想查看目前已运行哪些迁移,可使用 migrate:status Artisan 命令:

shell
php artisan migrate:status

若只想查看迁移将执行的 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 对象:

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 方法判断表、列或索引是否存在:

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 时,可用 charsetcollation 属性指定所创建表的字符集与排序规则:

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

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

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 时,可传入 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 方法会创建带有给定空间类型与 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}_typeVARCHAR 等价列。{column}_id 的列类型将根据模型主键类型为 UNSIGNED BIGINTCHAR(36)CHAR(26)

该方法用于定义多态 Eloquent 关联 所需的列。在下面的例子中,会创建 taggable_idtaggable_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_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}_idCHAR(26) 等价列,以及 {column}_typeVARCHAR 等价列。

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

$table->ulidMorphs('taggable');

uuidMorphs() {.collection-method}

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

该方法用于定义使用 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();
});

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

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

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

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

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 表上定义引用 usersid 列的 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\MigrationsStartedA batch of migrations is about to be executed.
Illuminate\Database\Events\MigrationsEndedA batch of migrations has finished executing.
Illuminate\Database\Events\MigrationStartedA single migration is about to be executed.
Illuminate\Database\Events\MigrationEndedA single migration has finished executing.
Illuminate\Database\Events\NoPendingMigrationsA migration command found no pending migrations.
Illuminate\Database\Events\SchemaDumpedA database schema dump has completed.
Illuminate\Database\Events\SchemaLoadedAn existing database schema dump has loaded.