数据库迁移
数据库: 迁移
简介
迁移就像数据库的版本控制,允许你的团队定义和共享应用程序的数据库架构定义。 如果你曾经不得不告诉团队成员在从代码控制中拉取更新后手动添加字段到他们的本地数据库,那么你就遇到了数据库迁移解决的问题。
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 数据库。
迁移结构
迁移类包含两个方法:up
和 down
。up
方法用于向数据库中添加新表、列或索引,而 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
{
// ...
}
执行迁移
执行 Artisan
的 migrate
命令,来运行所有未执行过的迁移:
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
/file
或array
缓存驱动作为你应用程序的默认缓存驱动。此外,所有的服务器必须与同一个中央缓存服务器进行通信。
在生产环境中执行强制迁移
有些迁移操作是破坏性的,这意味着它们可能会导致数据丢失。为了防止你对生产数据库运行这些命令,在执行这些命令之前,系统将提示你进行确认。如果要在运行强制命令的时候去掉提示,需要加上 --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
命令在删去所有数据表的过程中,会无视它们的前缀。如果数据库涉及到其它应用,使用该命令须十分小心。
数据表
创建数据表
接下来我们将使用 Schema
的 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();
});
创建表时,可以使用数据库结构构建器的 列方法 来定义表的列。
检查表 / 列是否存在
你可以使用 hasTable
和 hasColumn
, 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);
要删除已存在的表,你可以使用 drop
或 dropIfExists
方法:
Schema::drop('users');
Schema::dropIfExists('users');
使用外键重命名表
在重命名表之前,应该确认表的所有外键约束在迁移文件中有一个显式的名称,而不是让 Laravel 去指定。否则,外键约束名称将引用旧表名。
字段
创建字段
门面 Schema
的 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 构建器 Illuminate\Database\Schema\Blueprint
提供了多种方法,用来创建表中对应类型的列。下面列出了所有可用的方法:
bigIncrements
bigInteger
binary
boolean
char
dateTimeTz
dateTime
date
decimal
double
enum
float
foreignId
foreignIdFor
foreignUlid
foreignUuid
geography
geometry
id
increments
integer
ipAddress
json
jsonb
longText
macAddress
mediumIncrements
mediumInteger
mediumText
morphs
nullableMorphs
nullableTimestamps
nullableUlidMorphs
nullableUuidMorphs
rememberToken
set
smallIncrements
smallInteger
softDeletesTz
softDeletes
string
text
timeTz
time
timestampTz
timestamp
timestampsTz
timestamps
tinyIncrements
tinyInteger
tinyText
unsignedBigInteger
unsignedInteger
unsignedMediumInteger
unsignedSmallInteger
unsignedTinyInteger
ulidMorphs
uuidMorphs
ulid
uuid
year
bigIncrements()
bigIncrements
方法创建一个自增的 UNSIGNED BIGINT(主键)等价列:
$table->bigIncrements('id');
bigInteger()
bigInteger
方法创建一个 BIGINT
类型的等价列:
$table->bigInteger('votes');
binary()
binary
方法创建一个 BLOB
类型的等价列:
$table->binary('photo');
在使用MySQL、MariaDB或SQL Server时,你可以传参 length
和 fixed
来创建 VARBINARY
或 BINARY
类型的等价列:
$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 BIGINT
、CHAR(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 BIGINT
、CHAR(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();
});
修改列时,必须明确包括你想要保留的所有修饰符在列定义上 - 任何缺失的属性都将被丢弃。例如,要保留 unsigned
、default
和 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_at 和 updated_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 |
已加载现有数据库模式转储 |
原文地址:cndocs/11.x/mi...
译文地址:cndocs/11.x/mi...