数据库迁移

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

数据库: 迁移

简介

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

Laravel 的 Schema facade 为所有 Laravel 支持的数据库系统的创建和操作表提供了不依赖于数据库的支持。通常情况下,迁移会使用 facade 来创建和修改数据表和字段。

生成迁移

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

php artisan make:migration create_flights_table

Laravel 将使用迁移文件的名称来猜测表名以及迁移是否会创建一个新表。如果 Laravel 能够从迁移文件的名称中确定表的名称,它将在生成的迁移文件中预填入指定的表,或者,你也可以直接在迁移文件中手动指定表名。

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

!注意
迁移时可以使用 stub publishing 自定义发布。

整合迁移

在构建应用程序时,可能会随着时间的推移积累越来越多的迁移。这可能会导致你的 database/migrations 目录因为数百次迁移而变得臃肿。你如果愿意的话,可以将迁移「压缩」到单个 SQL 文件中。如果你想这样做,请先执行schema:dump 命令:

php artisan schema:dump

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

执行此命令时,Laravel 将向应用程序的 database/schema 目录写入一个「schema」文件。现在,当你尝试迁移数据库而没有执行其他迁移时,Laravel 将首先执行模式文件的 SQL 语句。在执行数据库结构文件的语句之后,Laravel 将执行不属于数据库结构的剩余的所有迁移。

如果你的应用程序的单元测试使用的数据库连接与你在本地开发过程中使用的不同,你应该确保你已经使用该数据库连接转存了一个 「schema」 文件,以便你的单元测试能够建立你的数据库。在进行本地开发的时候,你可能希望在数据库执行「schema」 文件之后再执行此操作:

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

你应该将数据库模式文件提交给源代码管理,以便团队中的其他新开发人员可以快速创建应用程序的初始数据库结构。

注意
整合迁移仅适用于 MySQL、PostgreSQL 和 SQLite 数据库,并使用数据库命令行的客户端。另外,数据库结构不能还原到内存中的 SQLite 数据库。

迁移结构

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

在这两种方法中,可以使用 Laravel 模式构建器来富有表现力地创建和修改表。要了解 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');
    }
};

设置迁移连接

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

/**
 * 指定数据库连接进行迁移
 *
 * @var string
 */
protected $connection = 'pgsql';

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

执行迁移

执行 Artisanmigrate 命令,来运行所有未执行过的迁移:

php artisan migrate

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

php artisan migrate:status

如果你希望在不实际运行迁移的情况下看到将被执行的 SQL 语句,你可以在 migrate 命令中提供 --pretend 选项。

php artisan migrate --pretend

在隔离的环境中执行迁移

如果你在多个服务器上部署你的应用程序,并在部署过程中运行迁移,你可能不希望两个服务器同时尝试迁移数据库。为了避免这种情况,你可以在调用 migrate 命令时使用 isolated 选项。

当提供 isolated 选项时,Laravel 将使用你的应用程序缓存驱动获得一个原子锁,然后再尝试运行你的迁移。所有其他试图运行 migrate 命令的尝试在锁被持有时都不会执行;然而,命令仍然会以成功的退出状态码退出:

php artisan migrate --isolated

注意
要使用这个功能,你的应用程序必须使用 memcached / redis / dynamodb / database / filearray 缓存驱动作为你应用程序的默认缓存驱动。此外,所有的服务器必须与同一个中央缓存服务器进行通信。

在生产环境中执行强制迁移

有些迁移操作是破坏性的,这意味着它们可能会导致数据丢失。为了防止你对生产数据库运行这些命令,在执行这些命令之前,系统将提示你进行确认。如果要在运行强制命令的时候去掉提示,需要加上 --force 标志:

php artisan migrate --force

回滚迁移

如果要回滚最后一次迁移操作,可以使用 Artisan rollback 命令。该命令会回滚最后「一批」的迁移,这可能包含多个迁移文件:

php artisan migrate:rollback

通过向 rollback 命令加上 step 参数,可以回滚指定数量的迁移。例如,以下命令将回滚最后五个迁移:

php artisan migrate:rollback --step=5

你可以通过向 rollback 命令提供 batch 选项来回滚特定的批次迁移,其中 batch 选项对应于应用程序中 migrations 数据库表中的一个批次值。例如,下面的命令将回滚第三批中的所有迁移。

 php artisan migrate:rollback --batch=3

如果你希望在不实际运行迁移的情况下看到将被执行的 SQL 语句,你可以添加--pretend 选项到 migrate:rollback 命令中:

php artisan migrate:rollback --pretend

migrate:reset 命令会回滚应用已运行过的所有迁移:

php artisan migrate:reset

使用单个命令同时进行回滚和迁移操作

migrate:refresh 命令首先会回滚已运行过的所有迁移,随后会执行 migrate。这一命令可以高效地重建你的整个数据库:

php artisan migrate:refresh

# 重置数据库,并运行所有的 seeds...
php artisan migrate:refresh --seed

通过在命令 refresh 中使用 step 参数,你可以回滚并重新执行指定数量的迁移操作。例如,下列命令会回滚并重新执行最后五个迁移操作:

php artisan migrate:refresh --step=5

删除所有表然后执行迁移

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

php artisan migrate:fresh

php artisan migrate:fresh --seed

默认情况下,migrate:fresh 命令仅会删除默认数据库连接中的表。然而,你可以使用 --database 选项来指定应该迁移的数据库连接。数据库连接名称应为 database 配置文件 中定义的连接。

php artisan migrate:fresh --database=admin

注意
migrate:fresh 命令在删去所有数据表的过程中,会无视它们的前缀。如果数据库涉及到其它应用,使用该命令须十分小心。

数据表

创建数据表

接下来我们将使用 Schemacreate 方法创建一个新的数据表。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();
});

创建表时,可以使用数据库结构构建器的 列方法 来定义表的列。

检查表 / 列是否存在

你可以使用 hasTablehasColumn, hasIndex 方法检查表或列或者索引是否存在:

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

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

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

        // ...
    });

charset 和 collation 属性可用于在使用 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 方法。目前只有 MySQL 和 Postgres 支持表注释:

    Schema::create('calculations', function (Blueprint $table) {
        $table->comment('业务计算');

        // ...
    });

更新数据表

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 去指定。否则,外键约束名称将引用旧表名。

字段

创建字段

门面 Schematable 方法可用于更新表。与 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 构建器 Illuminate\Database\Schema\Blueprint 提供了多种方法,用来创建表中对应类型的列。下面列出了所有可用的方法:

bigIncrements()

bigIncrements 方法创建一个自增的 UNSIGNED BIGINT(主键)等价列:

$table->bigIncrements('id');

bigInteger()

bigInteger 方法创建一个 BIGINT 类型的等价列:

$table->bigInteger('votes');

binary()

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

boolean 方法创建一个 BOOLEAN 类型的等价列:

$table->boolean('confirmed');

char()

char 方法创建一个给定长度的 CHAR 类型的等价列:

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

dateTimeTz()

dateTimeTz 方法创建一个带有时区信息的 DATETIME 类型的等价列,并可选择性地传参 precision 设置精度:

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

dateTime()

dateTime 方法创建一个 DATETIME 类型的等价列,并可选择性地传参 precision 设置精度:

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

date()

The date method creates a DATE equivalent column:
date 方法创建一个 DATE 类型的等价列。

$table->date('created_at');

decimal()

decimal 方法创建一个 DECIMAL 类型的等价列,它具有指定的精度(总位数)和小数位数(小数点后的位数)

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

double()

double 方法创建一个 DOUBLE 类型的等价列:

$table->double('amount');

enum()

enum 方法创建一个具有给定有效值的 ENUM 等效列:

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

float()

float 方法创建一个具有给定精度的 FLOAT 等效列:

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

foreignId()

foreignId 方法创建一个 UNSIGNED BIGINT 等效列:

$table->foreignId('user_id');

foreignIdFor()

foreignIdFor 方法为给定的模型类添加一个 {column}_id 等效列。列类型将是 UNSIGNED BIGINTCHAR(36) 或 CHAR(26),这取决于模型键类型:

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

foreignUlid()

foreignUlid 方法创建一个 ULID 等效列:

$table->foreignUlid('user_id');

foreignUuid()

foreignUuid 方法创建一个 UUID 等效列:

$table->foreignUuid('user_id');

geography()

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

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

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

geometry()

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

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

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

id()

id 方法是 bigIncrements 方法的别名。默认情况下,该方法将创建一个 id 列;但是,如果你想要为该列分配一个不同的名称,可以传递一个列名称:

$table->id();

increments()

increments 方法创建一个自动增长的 UNSIGNED INTEGER 等效列作为主键:

$table->increments('id');

integer()

integer 方法创建一个 INTEGER 等效列:

$table->integer('votes');

ipAddress()

ipAddress 方法创建一个 VARCHAR 等效列:

$table->ipAddress('visitor');

使用 PostgreSQL 时,将创建一个 INET 列。

json()

json 方法创建一个 JSON 等效列:

$table->json('options');

jsonb()

jsonb 方法创建一个 JSONB 等效列:

$table->jsonb('options');

longText()

longText 方法创建一个 LONGTEXT 等效列:

$table->longText('description');

使用 MySQL 或 MariaDB 时,你可以应用 binary 字符集到该列,以创建等同于 LONGBLOB 的列:

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

macAddress()

macAddress 方法创建一个旨在存储 MAC 地址的列。某些数据库系统(如 PostgreSQL)具有用于此类数据的专用列类型。其他数据库系统将使用等同于字符串的列:

$table->macAddress('device');

mediumIncrements()

mediumIncrements 方法创建一个自增的等同于 UNSIGNED MEDIUMINT 的主键列:

$table->mediumIncrements('id');

mediumInteger()

mediumInteger 方法创建一个等同于 MEDIUMINT 的列:

$table->mediumInteger('votes');

mediumText()

mediumText 方法创建一个等同于 MEDIUMTEXT 的列:

$table->mediumText('description');

使用 MySQL 或 MariaDB 时,你可以应用 binary 字符集到该列,以创建等同于 MEDIUMBLOB 的列:

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

morphs()

morphs 方法是一个便利方法,添加一个 {column}_id 等同列和一个等同于 VARCHAR 的  {column}_type 的列。{column}_id 的列类型将根据模型键类型而定,可能是 UNSIGNED BIGINTCHAR(36) 或 CHAR(26)

这个方法旨在用于定义多态 Eloquent 关系 所需的列。在以下示例中,将创建 taggable_id 和 taggable_type 列:

$table->morphs('taggable');

nullableTimestamps()

nullableTimestamps 方法是 timestamps方法的别名:

$table->nullableTimestamps(precision: 0);

nullableMorphs()

此方法类似于 morphs 方法;然而,创建的列将是「nullable」:

$table->nullableMorphs('taggable');

nullableUlidMorphs()

此方法类似于 ulidMorphs 方法;然而,创建的列将是「nullable」:

$table->nullableUlidMorphs('taggable');

nullableUuidMorphs()

此方法类似于 uuidMorphs方法;然而,创建的列将是「nullable」:

$table->nullableUuidMorphs('taggable');

rememberToken()

rememberToken 方法创建一个可为空的 VARCHAR(100) 等同列,用于存储当前的「记住我」认证令牌

$table->rememberToken();

set()

set 方法创建一个 SET 等同列,并附有给定的有效值列表:

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

smallIncrements()

smallIncrements 方法创建一个自增的 UNSIGNED SMALLINT 等效列作为主键:

$table->smallIncrements('id');

smallInteger()

smallInteger 方法创建一个 SMALLINT 等效列:

$table->smallInteger('votes');

softDeletesTz()

softDeletesTz 方法添加了一个可以为空的 deleted_at TIMESTAMP(带有时区)等效的列,具有可选小数秒精度。此列旨在存储 Eloquent 「软删除」功能所需的 deleted_at 时间戳:

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

softDeletes()

softDeletes 方法添加了一个可以为空的 deleted_at TIMESTAMP 等效列,具有可选小数秒精度。此列旨在存储 Eloquent 「软删除」功能所需的 deleted_at 时间戳:

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

string()

string 方法创建了一个给定长度的 VARCHAR 等效列:

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

text()

text 方法创建了一个 TEXT 等效列:

$table->text('description');

在使用 MySQL 或 MariaDB 时,你可以将 binary 字符集应用于列,以创建一个 BLOB 等效列:

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

timeTz()

timeTz 方法创建了一个带有可选小数秒精度的 TIME(带有时区)等效列:

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

time()

time 方法创建了一个带有可选小数秒精度的 TIME 等效列:

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

timestampTz()

timestampTz 方法创建了一个带有可选小数秒精度的 TIMESTAMP(带有时区)等效列:

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

timestamp()

timestamp 方法创建了一个带有可选小数秒精度的 TIMESTAMP 等效列:

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

timestampsTz()

timestampsTz 方法创建了带有可选小数秒精度 created_at 和 updated_at TIMESTAMP(带有时区)等效的列:

$table->timestampsTz(precision: 0);

timestamps()

timestamps 方法创建了带有可选小数秒精度 created_at 和 updated_at TIMESTAMP 等效列:

$table->timestamps(precision: 0);

tinyIncrements()

tinyIncrements 方法创建了一个自增的 UNSIGNED TINYINT 等效列作为主键:

$table->tinyIncrements('id');

tinyInteger()

tinyInteger 方法创建了一个 TINYINT 等效列:

$table->tinyInteger('votes');

tinyText()

tinyText 方法创建了一个 TINYTEXT 等效列:

$table->tinyText('notes');

在使用 MySQL 或 MariaDB 时,你可以将 binary 字符集应用于列,以创建一个 TINYBLOB 等效列:

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

unsignedBigInteger()

unsignedBigInteger 方法创建了一个 UNSIGNED BIGINT 等效列:

$table->unsignedBigInteger('votes');

unsignedInteger()

unsignedInteger 方法创建了一个 UNSIGNED INTEGER 等效列:

$table->unsignedInteger('votes');

unsignedMediumInteger()

unsignedMediumInteger 方法创建了一个 UNSIGNED MEDIUMINT 等效列:

$table->unsignedMediumInteger('votes');

unsignedSmallInteger()

unsignedSmallInteger 方法创建了一个 UNSIGNED SMALLINT 等效列:

$table->unsignedSmallInteger('votes');

unsignedTinyInteger()

unsignedTinyInteger 方法创建了一个 UNSIGNED TINYINT 等效列:

$table->unsignedTinyInteger('votes');

ulidMorphs()

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

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

$table->ulidMorphs('taggable');

uuidMorphs()

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

此方法旨在定义使用 UUID 标识符的多态 Eloquent 关系 所需的列时使用。在以下示例中,将创建 taggable_id 和 taggable_type 列:

$table->uuidMorphs('taggable');

ulid()

ulid 方法创建一个 ULID 等效列:

$table->ulid('id');

uuid()

uuid 方法创建一个 UUID 等效列:

$table->uuid('id');

year()

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') 在另一列之后放置该列(仅限 MySQL)。
->autoIncrement() 设置整数列为自动增长的(主键)。
->charset('utf8mb4') 为列指定字符集(仅限 MySQL)。
->collation('utf8mb4_unicode_ci') 为列指定排序规则。
->comment('my comment') 向列添加注释(MySQL / PostgreSQL)。
->default($value) 为列指定「默认」值。
->first() 将列置于表的「第一列」(仅限 MySQL)。
->from($integer) 设置自动增长字段的起始值(MySQL / PostgreSQL)。
->invisible() 使列在 SELECT * 查询中「不可见」(仅限 MySQL)。
->nullable($value = true) 允许插入 NULL 值到列中。
->storedAs($expression) 创建一个存储生成的列(MySQL / PostgreSQL / SQLite)。
->unsigned() 设置整数列为 UNSIGNED(仅限 MySQL)。
->useCurrent() 设置时间戳列使用 CURRENT_TIMESTAMP 作为默认值。
->useCurrentOnUpdate() 设置记录更新时,时间戳列使用 CURRENT_TIMESTAMP(仅限 MySQL)。
->virtualAs($expression) 创建一个虚拟生成的列(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();
        });
    }
};

注意
默认表达式的支持取决于你的数据库驱动程序、数据库版本和字段类型。请参阅你的数据库文档。

列顺序

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

修改列时,必须明确包括你想要保留的所有修饰符在列定义上 - 任何缺失的属性都将被丢弃。例如,要保留 unsigneddefault 和 comment 属性,你必须在更改列时明确调用每个修饰符:

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_id 和 morphable_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'); 添加一个全文索引(MySQL / PostgreSQL)。
$table->fullText('body')->language('english'); 添加一个指定语言的全文索引(PostgreSQL)。
$table->spatialIndex('location'); 添加一个空间索引(SQLite 除外)。

重命名索引

要重命名索引,可以使用模式构建器提供的 renameIndex 方法。此方法接受当前索引名称作为其第一个参数,所需名称作为其第二个参数:

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

删除索引

要删除索引,必须指定索引的名称。默认情况下,Laravel 会自动根据表名、索引的列名和索引类型分配索引名称。这里有一些例子:

命令 描述
$table->dropPrimary('users_id_primary'); 从「users」表中删除主键
$table->dropUnique('users_email_unique'); 从「users」表中删除 unique 索引
$table->dropIndex('geo_state_index'); 从「geo」表中删除基本索引
$table->dropFullText('posts_body_fulltext'); 从「post」表中删除一个全文索引
$table->dropSpatialIndex('geo_location_spatialindex'); 从「geo」表中删除空间索引(不支持 SQLite)

如果你传递一个列数组到删除索引的方法中,基于表名,列和索引类型的传统索引名称将被生成:

Schema::table('geo', function (Blueprint $table) {
    $table->dropIndex(['state']); // Drops index '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');
});

由于这种外键约束的定义方式过于繁复,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');

还为这些操作提供了另一种表达性语法:

方法 说明
$table->cascadeOnUpdate(); 更新应该级联
$table->restrictOnUpdate(); 更新应受限
$table->noActionOnUpdate(); 更新不执行任何动作
$table->cascadeOnDelete(); 删除应级联
$table->restrictOnDelete(); 删除应受限
$table->nullOnDelete(); 删除应将外键值设为 null

当使用任意 列修饰符 的时候,必须在调用 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 () {
    // 闭包中禁用的约束…
});

注意
SQLite 默认禁用外键约束。使用 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 数据库模式转储已完成
Illuminate\Database\Events\SchemaLoaded 已加载现有数据库模式转储

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

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

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

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

上一篇 下一篇
《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
贡献者:6
讨论数量: 0
发起讨论 查看所有版本


暂无话题~