数据库:迁移
介绍
迁移就像是数据库的版本控制,允许你的团队定义和共享应用程序的数据库架构定义。如果你曾经需要告诉队友在从源代码控制中拉取你的更改后手动向他们的本地数据库架构添加一列,那么你就遇到了数据库迁移解决的问题。
Laravel的Schema facade提供了数据库无关的支持,用于在所有支持的数据库系统中创建和操作表。通常,迁移将使用这个facade来创建和修改数据库表和列。
生成迁移
你可以使用make:migration Artisan命令来生成数据库迁移。新的迁移将被放置在你的database/migrations目录中。每个迁移文件名包含一个时间戳,允许Laravel确定迁移的顺序:
php artisan make:migration create_flights_tableLaravel将使用迁移的名称来尝试猜测表的名称以及迁移是否会创建一个新表。如果Laravel能够从迁移名称中确定表名,Laravel将预填充生成的迁移文件中指定的表。否则,你可以在迁移文件中手动指定表。
如果你想为生成的迁移指定一个自定义路径,可以在执行make:migration命令时使用--path选项。给定的路径应该是相对于应用程序的基路径的。
NOTE
迁移存根可以使用存根发布进行自定义。
压缩迁移
随着你构建应用程序,可能会随着时间的推移积累越来越多的迁移。这可能导致你的database/migrations目录变得臃肿,可能包含数百个迁移。如果你愿意,你可以将迁移“压缩”成一个SQL文件。要开始,执行schema:dump命令:
php artisan schema:dump
# 转储当前数据库架构并修剪所有现有迁移...
php artisan schema:dump --prune当你执行此命令时,Laravel会将一个“架构”文件写入应用程序的database/schema目录。架构文件的名称将对应于数据库连接。现在,当你尝试迁移数据库且没有其他迁移被执行时,Laravel将首先执行你正在使用的数据库连接的架构文件中的SQL语句。在执行架构文件的SQL语句后,Laravel将执行任何不属于架构转储的剩余迁移。
如果你的应用程序测试使用的数据库连接与本地开发期间通常使用的不同,你应该确保使用该数据库连接转储了一个架构文件,以便你的测试能够构建你的数据库。你可能希望在转储本地开发期间通常使用的数据库连接后执行此操作:
php artisan schema:dump
php artisan schema:dump --database=testing --prune你应该将数据库架构文件提交到源代码控制,以便团队中的其他新开发人员可以快速创建应用程序的初始数据库结构。
WARNING
迁移压缩仅适用于MariaDB、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
{
// ...
}跳过迁移
有时,某个迁移可能是用于支持尚未启用的功能,而你暂时不希望它执行。在这种情况下,你可以在迁移中定义一个 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将在尝试运行迁移之前使用应用程序的缓存驱动程序获取一个原子锁。在持有该锁时,所有其他尝试运行migrate命令的操作都不会执行;然而,命令仍将以成功的退出状态代码退出:
php artisan migrate --isolatedWARNING
要使用此功能,应用程序必须使用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 --pretendmigrate: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=adminWARNING
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和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();
});此外,还可以使用其他一些属性和方法来定义表创建的其他方面。在使用MariaDB或MySQL时,可以使用engine属性指定表的存储引擎:
Schema::create('users', function (Blueprint $table) {
$table->engine('InnoDB');
// ...
});在使用MariaDB或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方法。表注释目前仅由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);要删除现有表,可以使用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');
});可用的列类型
架构构建器蓝图提供了多种方法,这些方法对应于可以添加到数据库表中的不同类型的列。每个可用的方法都列在下表中:
布尔类型
字符串和文本类型
数字类型
bigIncrementsbigIntegerdecimaldoublefloatidincrementsintegermediumIncrementsmediumIntegersmallIncrementssmallIntegertinyIncrementstinyIntegerunsignedBigIntegerunsignedIntegerunsignedMediumIntegerunsignedSmallIntegerunsignedTinyInteger
日期和时间类型
二进制类型
对象和Json类型
UUID和ULID类型
空间类型
关系类型
特殊类型
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(带时区)等效列,具有可选的分秒精度:
$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', total: 8, places: 2);double()
double方法创建一个DOUBLE等效列:
$table->double('amount');enum()
enum方法创建一个具有给定有效值的ENUM等效列:
$table->enum('difficulty', ['easy', 'hard']);当然,您可以使用 Enum::cases() 方法,而不是手动定义允许值的数组:
use App\Enums\Difficulty;
$table->enum('difficulty', Difficulty::cases());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);NOTE
对空间类型的支持取决于你的数据库驱动程序。请参考数据库的文档。如果你的应用程序使用的是PostgreSQL数据库,必须安装PostGIS扩展才能使用geography方法。
geometry()
geometry方法创建一个具有给定空间类型和SRID(空间参考系统标识符)的GEOMETRY等效列:
$table->geometry('positions', subtype: 'point', srid: 0);NOTE
对空间类型的支持取决于你的数据库驱动程序。请参考数据库的文档。如果你的应用程序使用的是PostgreSQL数据库,必须安装PostGIS扩展才能使用geometry方法。
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');在使用SQLite时,将创建一个TEXT列。
jsonb()
jsonb方法创建一个JSONB等效列:
$table->jsonb('options');在使用SQLite时,将创建一个TEXT列。
longText()
longText方法创建一个LONGTEXT等效列:
$table->longText('description');在使用MySQL或MariaDB时,可以为列应用binary字符集以创建一个LONGBLOB等效列:
$table->longText('data')->charset('binary'); // LONGBLOBmacAddress()
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'); // MEDIUMBLOBmorphs()
morphs方法是一个便捷方法,添加一个{column}_id等效列和一个{column}_type VARCHAR等效列。{column}_id的列类型将是UNSIGNED BIGINT、CHAR(36)或CHAR(26),具体取决于模型键类型。
此方法旨在用于定义多态Eloquent关系所需的列。在以下示例中,将创建taggable_id和taggable_type列:
$table->morphs('taggable');nullableMorphs()
该方法类似于morphs方法;然而,创建的列将是“可为空”的:
$table->nullableMorphs('taggable');nullableUlidMorphs()
该方法类似于ulidMorphs方法;然而,创建的列将是“可为空”的:
$table->nullableUlidMorphs('taggable');nullableUuidMorphs()
该方法类似于uuidMorphs方法;然而,创建的列将是“可为空”的:
$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'); // BLOBtimeTz()
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'); // TINYBLOBunsignedBigInteger()
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');vector()
vector方法创建一个vector等效列:
$table->vector('embedding', dimensions: 100);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') | 将列放置在另一个列“之后”(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();
});
}
};WARNING
对默认表达式的支持取决于你的数据库驱动程序、数据库版本和字段类型。请参考数据库的文档。
列顺序
在使用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();
});在修改列时,必须显式包含要保留在列定义中的所有修饰符 - 任何缺失的属性都将被删除。例如,要保留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'); | 添加全文索引(MariaDB / 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”表中删除唯一索引。 |
$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->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']);切换外键约束
可以通过使用以下方法在迁移中启用或禁用外键约束:
Schema::enableForeignKeyConstraints();
Schema::disableForeignKeyConstraints();
Schema::withoutForeignKeyConstraints(function () {
// 在此闭包中禁用约束...
});WARNING
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 | 已加载现有的数据库架构转储。 |