数据库:迁移
介绍
迁移就像是数据库的版本控制,允许您的团队定义和共享应用程序的数据库架构定义。如果您曾经需要告诉队友在从源代码控制中拉取您的更改后手动向他们的本地数据库架构添加一列,那么您就遇到了数据库迁移解决的问题。
Laravel 的 Schema
facade 提供了与数据库无关的支持,用于在所有 Laravel 支持的数据库系统中创建和操作表。通常,迁移将使用此 facade 来创建和修改数据库表和列。
生成迁移
您可以使用 make:migration
Artisan 命令 生成数据库迁移。新迁移将放置在您的 database/migrations
目录中。每个迁移文件名都包含一个时间戳,允许 Laravel 确定迁移的顺序:
php artisan make:migration create_flights_table
Laravel 将使用迁移的名称尝试猜测表的名称以及迁移是否会创建新表。如果 Laravel 能够从迁移名称中确定表名,Laravel 将预填充生成的迁移文件中指定的表。否则,您可以在迁移文件中手动指定表。
如果您想为生成的迁移指定自定义路径,可以在执行 make:migration
命令时使用 --path
选项。给定的路径应相对于应用程序的基路径。
可以使用 存根发布 自定义迁移存根。
压缩迁移
随着应用程序的构建,您可能会随着时间的推移积累越来越多的迁移。这可能导致您的 database/migrations
目录变得臃肿,可能包含数百个迁移。如果您愿意,您可以将迁移 "压缩" 为一个 SQL 文件。要开始,请执行 schema:dump
命令:
php artisan schema:dump
# 转储当前数据库架构并修剪所有现有迁移...
php artisan schema:dump --prune
执行此命令时,Laravel 会将 "schema" 文件写入应用程序的 database/schema
目录。架构文件的名称将对应于数据库连接。现在,当您尝试迁移数据库且没有其他迁移被执行时,Laravel 将首先执行您正在使用的数据库连接的架构文件中的 SQL 语句。在执行架构文件的 SQL 语句后,Laravel 将执行不属于架构转储的任何剩余迁移。
如果应用程序的测试使用的数据库连接与您在本地开发期间通常使用的数据库连接不同,您应确保使用该数据库连接转储了架构文件,以便测试能够构建数据库。您可能希望在转储您通常在本地开发期间使用的数据库连接后执行此操作:
php artisan schema:dump
php artisan schema:dump --database=testing --prune
您应将数据库架构文件提交到源代码控制,以便团队中的其他新开发人员可以快速创建应用程序的初始数据库结构。
迁移压缩仅适用于 MySQL、PostgreSQL 和 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
{
// ...
}
运行迁移
要运行所有未完成的迁移,请执行 migrate
Artisan 命令:
php artisan migrate
如果您想查看到目前为止已运行的迁移,可以使用 migrate:status
Artisan 命令:
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
回滚迁移
要回滚最新的迁移操作,可以使用 rollback
Artisan 命令。此命令回滚最后一个 "批次" 的迁移,其中可能包含多个迁移文件:
php artisan migrate:rollback
您可以通过为 rollback
命令提供 step
选项来回滚有限数量的迁移。例如,以下命令将回滚最近的五个迁移:
php artisan migrate:rollback --step=5
您可以通过为 rollback
命令提供 batch
选项来回滚特定的 "批次" 迁移,其中 batch
选项对应于应用程序的 migrations
数据库表中的批次值。例如,以下命令将回滚批次三中的所有迁移:
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
# 刷新数据库并运行所有数据库种子...
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
方法接受两个参数:第一个是表的名称,第二个是接收 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
方法确定表或列的存在:
if (Schema::hasTable('users')) {
// "users" 表存在...
}
if (Schema::hasColumn('users', 'email')) {
// "users" 表存在并且有一个 "email" 列...
}
数据库连接和表选项
如果您想在不是应用程序默认连接的数据库连接上执行架构操作,请使用 connection
方法:
Schema::connection('sqlite')->create('users', function (Blueprint $table) {
$table->id();
});
此外,还可以使用其他一些属性和方法来定义表创建的其他方面。使用 MySQL 时,可以使用 engine
属性指定表的存储引擎:
Schema::create('users', function (Blueprint $table) {
$table->engine = 'InnoDB';
// ...
});
使用 MySQL 时,可以使用 charset
和 collation
属性指定创建表的字符集和排序规则:
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('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);
要删除现有表,可以使用 drop
或 dropIfExists
方法:
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');
});
可用列类型
架构构建器蓝图提供了多种方法,分别对应于可以添加到数据库表的不同类型的列。每个可用方法都列在下表中:
bigIncrementsbigIntegerbinarybooleanchardateTimeTzdateTimedatedecimaldoubleenumfloatforeignIdforeignIdForforeignUlidforeignUuidgeometryCollectiongeometryidincrementsintegeripAddressjsonjsonblineStringlongTextmacAddressmediumIncrementsmediumIntegermediumTextmorphsmultiLineStringmultiPointmultiPolygonnullableMorphsnullableTimestampsnullableUlidMorphsnullableUuidMorphspointpolygonrememberTokensetsmallIncrementssmallIntegersoftDeletesTzsoftDeletesstringtexttimeTztimetimestampTztimestamptimestampsTztimestampstinyIncrementstinyIntegertinyTextunsignedBigIntegerunsignedDecimalunsignedIntegerunsignedMediumIntegerunsignedSmallIntegerunsignedTinyIntegerulidMorphsuuidMorphsuliduuidyear
bigIncrements()
bigIncrements
方法创建一个自动递增的 UNSIGNED BIGINT
(主键)等效列:
$table->bigIncrements('id');
bigInteger()
bigInteger
方法创建一个 BIGINT
等效列:
$table->bigInteger('votes');
binary()
binary
方法创建一个 BLOB
等效列:
$table->binary('photo');
boolean()
boolean
方法创建一个 BOOLEAN
等效列:
$table->boolean('confirmed');
char()
char
方法创建一个给定长度的 CHAR
等效列:
$table->char('name', 100);
dateTimeTz()
dateTimeTz
方法创建一个 DATETIME
(带时区)等效列,具有可选的精度(总位数):
$table->dateTimeTz('created_at', $precision = 0);
dateTime()
dateTime
方法创建一个 DATETIME
等效列,具有可选的精度(总位数):
$table->dateTime('created_at', $precision = 0);
date()
date
方法创建一个 DATE
等效列:
$table->date('created_at');
decimal()
decimal
方法创建一个具有给定精度(总位数)和小数位数的 DECIMAL
等效列:
$table->decimal('amount', $precision = 8, $scale = 2);
double()
double
方法创建一个具有给定精度(总位数)和小数位数的 DOUBLE
等效列:
$table->double('amount', 8, 2);
enum()
enum
方法创建一个具有给定有效值的 ENUM
等效列:
$table->enum('difficulty', ['easy', 'hard']);
float()
float
方法创建一个具有给定精度(总位数)和小数位数的 FLOAT
等效列:
$table->float('amount', 8, 2);
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');
geometryCollection()
geometryCollection
方法创建一个 GEOMETRYCOLLECTION
等效列:
$table->geometryCollection('positions');
geometry()
geometry
方法创建一个 GEOMETRY
等效列:
$table->geometry('positions');
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');
使用 Postgres 时,将创建一个 INET
列。
json()
json
方法创建一个 JSON
等效列:
$table->json('options');
jsonb()
jsonb
方法创建一个 JSONB
等效列:
$table->jsonb('options');
lineString()
lineString
方法创建一个 LINESTRING
等效列:
$table->lineString('positions');
longText()
longText
方法创建一个 LONGTEXT
等效列:
$table->longText('description');
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');
morphs()
morphs
方法是一个便捷方法,添加一个 {column}_id
等效列和一个 {column}_type
VARCHAR
等效列。{column}_id
的列类型将根据模型键类型为 UNSIGNED BIGINT
、CHAR(36)
或 CHAR(26)
。
此方法旨在用于定义多态 Eloquent 关系 所需的列。在以下示例中,将创建 taggable_id
和 taggable_type
列:
$table->morphs('taggable');
multiLineString()
multiLineString
方法创建一个 MULTILINESTRING
等效列:
$table->multiLineString('positions');
multiPoint()
multiPoint
方法创建一个 MULTIPOINT
等效列:
$table->multiPoint('positions');
multiPolygon()
multiPolygon
方法创建一个 MULTIPOLYGON
等效列:
$table->multiPolygon('positions');
nullableTimestamps()
nullableTimestamps
方法是 timestamps 方法的别名:
$table->nullableTimestamps(0);
nullableMorphs()
该方法类似于 morphs 方法;但是,创建的列将是 "nullable":
$table->nullableMorphs('taggable');
nullableUlidMorphs()
该方法类似于 ulidMorphs 方法;但是,创建的列将是 "nullable":
$table->nullableUlidMorphs('taggable');
nullableUuidMorphs()
该方法类似于 uuidMorphs 方法;但是,创建的列将是 "nullable":
$table->nullableUuidMorphs('taggable');
point()
point
方法创建一个 POINT
等效列:
$table->point('position');
polygon()
polygon
方法创建一个 POLYGON
等效列:
$table->polygon('position');
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($column = 'deleted_at', $precision = 0);
softDeletes()
softDeletes
方法添加一个可为空的 deleted_at
TIMESTAMP
等效列,具有可选的精度(总位数)。此列旨在存储 Eloquent 的 "软删除" 功能所需的 deleted_at
时间戳:
$table->softDeletes($column = 'deleted_at', $precision = 0);
string()
string
方法创建一个给定长度的 VARCHAR
等效列:
$table->string('name', 100);
text()
text
方法创建一个 TEXT
等效列:
$table->text('description');
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');
unsignedBigInteger()
unsignedBigInteger
方法创建一个 UNSIGNED BIGINT
等效列:
$table->unsignedBigInteger('votes');
unsignedDecimal()
unsignedDecimal
方法创建一个 UNSIGNED DECIMAL
等效列,具有可选的精度(总位数)和小数位数:
$table->unsignedDecimal('amount', $precision = 8, $scale = 2);
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",可以使用 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() | 将 INTEGER 列设置为自动递增(主键)。 |
->charset('utf8mb4') | 为列指定字符集(MySQL)。 |
->collation('utf8mb4_unicode_ci') | 为列指定排序规则(MySQL/PostgreSQL/SQL Server)。 |
->comment('my comment') | 为列添加注释(MySQL/PostgreSQL)。 |
->default($value) | 为列指定 "默认" 值。 |
->first() | 将列放置在表的 "首位"(MySQL)。 |
->from($integer) | 设置自动递增字段的起始值(MySQL / PostgreSQL)。 |
->invisible() | 使列对 SELECT * 查询 "不可见"(MySQL)。 |
->nullable($value = true) | 允许将 NULL 值插入列中。 |
->storedAs($expression) | 创建存储生成的列(MySQL / PostgreSQL)。 |
->unsigned() | 将 INTEGER 列设置为 UNSIGNED(MySQL)。 |
->useCurrent() | 将 TIMESTAMP 列设置为使用 CURRENT_TIMESTAMP 作为默认值。 |
->useCurrentOnUpdate() | 在记录更新时将 TIMESTAMP 列设置为使用 CURRENT_TIMESTAMP(MySQL)。 |
->virtualAs($expression) | 创建虚拟生成的列(MySQL / PostgreSQL / SQLite)。 |
->generatedAs($expression) | 使用指定的序列选项创建标识列(PostgreSQL)。 |
->always() | 定义序列值相对于输入的优先级(PostgreSQL)。 |
->isGeometry() | 将空间列类型设置为 geometry - 默认类型为 geography (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();
});
在 SQLite 上修改列
如果您的应用程序使用 SQLite 数据库,您必须使用 Composer 包管理器安装 doctrine/dbal
包,然后才能修改列。Doctrine DBAL 库用于确定列的当前状态,并创建所需的 SQL 查询以对列进行请求的更改:
composer require doctrine/dbal
如果您计划修改使用 timestamp
方法创建的列,您还必须将以下配置添加到应用程序的 config/database.php
配置文件中:
use Illuminate\Database\DBAL\TimestampType;
'dbal' => [
'types' => [
'timestamp' => TimestampType::class,
],
],
使用 doctrine/dbal
包时,可以修改以下列类型:bigInteger
、binary
、boolean
、char
、date
、dateTime
、dateTimeTz
、decimal
、double
、integer
、json
、longText
、mediumText
、smallInteger
、string
、text
、time
、tinyText
、unsignedBigInteger
、unsignedInteger
、unsignedSmallInteger
、ulid
和 uuid
。
重命名列
要重命名列,可以使用架构构建器提供的 renameColumn
方法:
Schema::table('users', function (Blueprint $table) {
$table->renameColumn('from', 'to');
});
在旧数据库上重命名列
如果您运行的数据库安装版本低于以下版本之一,您应确保在重命名列之前通过 Composer 包管理器安装了 doctrine/dbal
库:
- MySQL <
8.0.3
- MariaDB <
10.5.2
- SQLite <
3.25.0
删除列
要删除列,可以在架构构建器上使用 dropColumn
方法:
Schema::table('users', function (Blueprint $table) {
$table->dropColumn('votes');
});
您可以通过将列名数组传递给 dropColumn
方法,从表中删除多个列:
Schema::table('users', function (Blueprint $table) {
$table->dropColumn(['votes', 'avatar', 'location']);
});
在旧数据库上删除列
如果您运行的 SQLite 版本低于 3.35.0
,您必须通过 Composer 包管理器安装 doctrine/dbal
包,然后才能使用 dropColumn
方法。使用此包时,在单个迁移中删除或修改多个列不受支持。
可用命令别名
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 外)。 |
索引长度和 MySQL / MariaDB
默认情况下,Laravel 使用 utf8mb4
字符集。如果您运行的 MySQL 版本低于 5.7.7 或 MariaDB 版本低于 10.2.2,您可能需要手动配置迁移生成的默认字符串长度,以便 MySQL 为其创建索引。您可以通过在 App\Providers\AppServiceProvider
类的 boot
方法中调用 Schema::defaultStringLength
方法来配置默认字符串长度:
use Illuminate\Support\Facades\Schema;
/**
* 启动任何应用程序服务。
*/
public function boot(): void
{
Schema::defaultStringLength(191);
}
或者,您可以为数据库启用 innodb_large_prefix
选项。有关如何正确启用此选项的说明,请参阅数据库的文档。
重命名索引
要重命名索引,可以使用架构构建器蓝图提供的 renameIndex
方法。此方法接受当前索引名称作为第一个参数,期望的名称作为第二个参数:
$table->renameIndex('from', 'to')
如果您的应用程序使用 SQLite 数据库,您必须通过 Composer 包管理器安装 doctrine/dbal
包,然后才能使用 renameIndex
方法。
删除索引
要删除索引,您必须指定索引的名称。默认情况下,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 外)。 |
如果您将列数组传递给删除索引的方法,将根据表名、列和索引类型生成常规索引名称:
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');
});
由于这种语法相当冗长,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 () {
// 在此闭包中禁用约束...
});
事件
为了方便起见,每个迁移操作都会调度一个 事件。以下所有事件都继承自基础 Illuminate\Database\Events\MigrationEvent
类:
类 | 描述 |
---|---|
Illuminate\Database\Events\MigrationsStarted | 一批迁移即将执行。 |
Illuminate\Database\Events\MigrationsEnded | 一批迁移已完成执行。 |
Illuminate\Database\Events\MigrationStarted | 单个迁移即将执行。 |
Illuminate\Database\Events\MigrationEnded | 单个迁移已完成执行。 |
Illuminate\Database\Events\SchemaDumped | 数据库架构转储已完成。 |
Illuminate\Database\Events\SchemaLoaded | 已加载现有数据库架构转储。 |