介绍
迁移就像数据库的版本控制,允许你的团队定义和共享应用程序的数据库架构定义。 如果你曾经不得不告诉团队成员在从代码控制中拉取更新后手动添加字段到他们的本地数据库,那么你就遇到了数据库迁移解决的问题。
Laravel Schema
facade 为所有 Laravel 支持的数据库系统的创建和操作表提供了不依赖于数据库的支持。通常情况下,迁移会使用 facade 来创建和修改数据表和字段。
生成迁移
你可以使用 make:migration
Artisan 命令 来生成数据库迁移。新的迁移文件将放在你的 database/migrations
目录下。每个迁移文件名都包含一个时间戳来使 Laravel 确定迁移的顺序:
php artisan make:migration create_flights_table
Laravel 将使用迁移文件的名称来猜测表名以及迁移是否会创建一个新表。如果 Laravel 能够从迁移文件的名称中确定表的名称,它将在生成的迁移文件中预填入指定的表,或者,你也可以直接在迁移文件中手动指定表名。
如果要为生成的迁移指定自定义路径,你可以在执行 make:migration
命令时使用 --path
选项。给定的路径应该相对于应用程序的基本路径。
整合迁移
在构建应用程序时,可能会随着时间的推移积累越来越多的迁移。这可能会导致你的 database/migrations
目录因为数百次迁移而变得臃肿。你如果愿意的话,可以将迁移「压缩」到单个 SQL 文件中。如果你想这样做,请先执行schema:dump
命令:
php artisan schema:dump # 转储当前数据库架构并删除所有现有迁移... php artisan schema:dump --prune
执行此命令时,Laravel 将向应用程序的 database/schema
目录写入一个「schema」文件。现在,当你尝试迁移数据库而没有执行其他迁移时,Laravel 将首先执行模式文件的 SQL 语句。在执行数据库结构文件的语句之后,Laravel 将执行不属于数据库结构的剩余的所有迁移。
如果你的应用程序的测试使用的数据库连接与你在本地开发过程中通常使用的不同,你应该确保你已经使用该数据库连接转储了一个 schema 文件,以便你的测试能够建立你的数据库。你可能希望在切换(dump)你在本地开发过程中通常使用的数据库连接之后再做这件事。
php artisan schema:dump php artisan schema:dump --database=testing --prune
你应该将数据库模式文件提交给源代码管理,以便团队中的其他新开发人员可以快速创建应用程序的初始数据库结构。
迁移结构
迁移类包含两个方法: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
属性:
/** * The database connection that should be used by the migration. * * @var string */ protected $connection = 'pgsql'; /** * 执行迁移 */ public function up(): void { // ... }
执行迁移
执行 Artisan 命令 migrate
,来运行所有未执行过的迁移:
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
有些迁移操作是破坏性的,这意味着它们可能会导致数据丢失。为了防止你对生产数据库运行这些命令,在执行这些命令之前,系统将提示你进行确认。如果要在运行强制命令的时候去掉提示,需要加上 --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
命令 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
数据表
创建数据表
接下来我们将使用 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
方法检查表或列是否存在:
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'; // ... });
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('Business calculations'); // ... });
更新数据表
Schema
门面的 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
方法接受两个参数:表名和一个闭包,该闭包接收一个 illumb\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
geometryCollection
geometry
id
increments
integer
ipAddress
json
jsonb
lineString
longText
macAddress
mediumIncrements
mediumInteger
mediumText
morphs
multiLineString
multiPoint
multiPolygon
nullableMorphs
nullableTimestamps
nullableUlidMorphs
nullableUuidMorphs
point
polygon
rememberToken
set
smallIncrements
smallInteger
softDeletesTz
softDeletes
string
text
timeTz
time
timestampTz
timestamp
timestampsTz
timestamps
tinyIncrements
tinyInteger
tinyText
unsignedBigInteger
unsignedDecimal
unsignedInteger
unsignedMediumInteger
unsignedSmallInteger
unsignedTinyInteger
ulidMorphs
uuidMorphs
ulid
uuid
year
bigIncrements
方法用于在数据表中创建一个自增的 UNSIGNED BIGINT
类型(主键)的列:
<code>bigInteger()</code> {.collection-method}$table->bigIncrements('id');
bigInteger
方法用于在数据表中创建一个 BIGINT
类型的列:
<code>binary()</code> {.collection-method}$table->bigInteger('votes');
binary
方法用于在数据表中创建一个 BLOB
类型的列:
<code>boolean()</code> {.collection-method}$table->binary('photo');
boolean
方法用于在数据表中创建一个 BOOLEAN
类型的列:
<code>char()</code> {.collection-method}$table->boolean('confirmed');
char
方法用于在数据表中创建一个 CHAR
类型的列,长度由参数指定:
<code>dateTimeTz()</code> {.collection-method}$table->char('name', 100);
dateTimeTz
方法用于在数据表中创建一个 DATETIME
类型(附有 timezone)的列,可选参数为精度的总位数:
<code>dateTime()</code> {.collection-method}$table->dateTimeTz('created_at', $precision = 0);
dateTime
方法用于在数据表中创建一个 DATETIME
类型的列,可选参数为精度的总位数:
<code>date()</code> {.collection-method}$table->dateTime('created_at', $precision = 0);
date
方法用于在数据表中创建一个 DATE
类型的列:
<code>decimal()</code> {.collection-method}$table->date('created_at');
decimal
方法用于在数据表中创建一个 DECIMAL
类型的列,可选参数分别为有效字数总位数、小数部分总位数:
<code>double()</code> {.collection-method}$table->decimal('amount', $precision = 8, $scale = 2);
double
方法用于在数据表中创建一个 DOUBLE
类型的列,可选参数分别为有效字数总位数、小数部分总位数:
<code>enum()</code> {.collection-method}$table->double('amount', 8, 2);
enum
方法用于在数据表中创建一个 ENUM
类型的列,合法的值列表由参数指定:
<code>float()</code> {.collection-method}$table->enum('difficulty', ['easy', 'hard']);
float
方法用于在数据表中创建一个 FLOAT
类型的列,可选参数分别为有效字数总位数、小数部分总位数:
<code>foreignId()</code> {.collection-method}$table->float('amount', 8, 2);
foreignId
方法是 unsignedBigInteger
的别名:
<code>foreignIdFor()</code> {.collection-method}$table->foreignId('user_id');
foreignIdFor
方法为给定模型类添加了 {column}_id UNSIGNED BIGINT
等效列:
<code>foreignUlid()</code> {.collection-method}$table->foreignIdFor(User::class);
foreignUuid
方法创建一个 UUID
等效列:
<code>foreignUuid()</code> {.collection-method}$table->foreignUlid('user_id');
geometryCollection
方法相当于 GEOMETRYCOLLECTION
:
<code>geometryCollection()</code> {.collection-method}$table->foreignUuid('user_id');
geometryCollection
方法相当于 GEOMETRYCOLLECTION
:
<code>geometry()</code> {.collection-method}$table->geometryCollection('positions');
geometry
方法相当于 GEOMETRY
:
<code>id()</code> {.collection-method}$table->geometry('positions');
id
方法是bigIncrements
的别名。默认情况下,该方法将创建一个 id
列; 但是,如果要为列指定不同的名称,则可以传递列名:
<code>increments()</code> {.collection-method}$table->id();
increments
方法创建一个自动递增相当于 UNSIGNED INTEGER
的列作为主键:
<code>integer()</code> {.collection-method}$table->increments('id');
integer
方法相当于 INTEGER
:
<code>ipAddress()</code> {.collection-method}$table->integer('votes');
ipAddress
方法相当于 VARCHAR
:
<code>json()</code> {.collection-method}$table->ipAddress('visitor');
json
方法相当于 JSON
:
<code>jsonb()</code> {.collection-method}$table->json('options');
jsonb
方法相当于 JSONB
:
<code>lineString()</code> {.collection-method}$table->jsonb('options');
lineString
方法相当于 LINESTRING
:
<code>longText()</code> {.collection-method}$table->lineString('positions');
longText
方法相当于 LONGTEXT
:
<code>macAddress()</code> {.collection-method}$table->longText('description');
macAddress
方法创建一个用于保存 MAC 地址的列。一些数据库系统(如 PostgreSQL),为这种类型的数据提供了专用的类型。其他数据库系统相当于使用字符串类型:
<code>mediumIncrements()</code> {.collection-method}$table->macAddress('device');
mediumIncrements
方法用于创建一个 UNSIGNED MEDIUMINT
类型的自动递增的列作为主键:
<code>mediumInteger()</code> {.collection-method}$table->mediumIncrements('id');
mediumInteger
方法用于创建一个 MEDIUMINT
类型的列:
<code>mediumText()</code> {.collection-method}$table->mediumInteger('votes');
mediumText
方法用于创建一个 MEDIUMTEXT
类型的列:
<code>morphs()</code> {.collection-method}$table->mediumText('description');
morphs
方法用于快速创建一个名称为 {column}_id
,类型为 UNSIGNED BIGINT
的列和一个名称为 {column}_type
,类型为 VARCHAR
的列。
这个方法在定义多态关联所需的列时使用。在下面的例子中, taggable_id
和 taggable_type
这两个列会被创建:
<code>multiLineString()</code> {.collection-method}$table->morphs('taggable');
multiLineString
方法用于创建一个 MULTILINESTRING
类型的列:
<code>multiPoint()</code> {.collection-method}$table->multiLineString('positions');
multiPoint
方法用于创建一个 MULTIPOINT
类型的列:
<code>multiPolygon()</code> {.collection-method}$table->multiPoint('positions');
multiPolygon
方法用于创建一个 MULTIPOLYGON
类型的列:
<code>nullableTimestamps()</code> {.collection-method}$table->multiPolygon('positions');
这个方法和 timestamps 方法类似;需要注意的是此方法创建的列是 nullable
的:
<code>nullableMorphs()</code> {.collection-method}$table->nullableTimestamps(0);
这个方法和 morphs 方法类似;需要注意的是此方法创建的列是 nullable
的:
<code>nullableUlidMorphs()</code> {.collection-method}$table->nullableMorphs('taggable');
这个方法和 ulidMorphs 方法类似;需要注意的是此方法创建的列是 nullable
。
<code>nullableUuidMorphs()</code> {.collection-method}$table->nullableUlidMorphs('taggable');
这个方法和 uuidMorphs 方法类似;需要注意的是此方法创建的列是 nullable
的:
<code>point()</code> {.collection-method}$table->nullableUuidMorphs('taggable');
point
方法相当于 POINT
:
<code>polygon()</code> {.collection-method}$table->point('position');
The polygon
method creates a POLYGON
equivalent column:
<code>rememberToken()</code> {.collection-method}$table->polygon('position');
添加一个允许空值的 VARCHAR (100)
类型的 remember_token
字段,用于存储 记住用户:
<code>set()</code> {.collection-method}$table->rememberToken();
set
方法使用给定的有效值列表创建一个 SET
等效列:
<code>smallIncrements()</code> {.collection-method}$table->set('flavors', ['strawberry', 'vanilla']);
smallIncrements
方法创建一个自动递增的 UNSIGNED SMALLINT
等效列作为主键:
<code>smallInteger()</code> {.collection-method}$table->smallIncrements('id');
smallInteger
方法创建一个 SMALLINT
等效列:
<code>softDeletesTz()</code> {.collection-method}$table->smallInteger('votes');
softDeletesTz
方法添加了一个可为空的 deleted_at
TIMESTAMP
(带时区)等效列,具有可选精度(总位数)。此列旨在存储 Eloquent 的“软删除”功能所需的 deleted_at
时间戳:
<code>softDeletes()</code> {.collection-method}$table->softDeletesTz($column = 'deleted_at', $precision = 0);
softDeletes
方法添加了一个可为空的 deleted_at
TIMESTAMP
等效列,具有可选精度(总位数)。此列旨在存储 Eloquent 的「软删除」功能所需的 deleted_at
时间戳,相当于为软删除添加一个可空的 deleted_at
字段:
<code>string()</code> {.collection-method}$table->softDeletes($column = 'deleted_at', $precision = 0);
string
方法创建一个给定长度的 VARCHAR
等效列,相当于指定长度的 VARCHAR:
<code>text()</code> {.collection-method}$table->string('name', 100);
text
方法创建一个 TEXT
等效列:
<code>timeTz()</code> {.collection-method}$table->text('description');
timeTz
方法创建一个具有可选精度(总位数)的 TIME
(带时区)等效列:
<code>time()</code> {.collection-method}$table->timeTz('sunrise', $precision = 0);
time
方法创建一个具有可选精度(总位数)的 TIME
等效列:
<code>timestampTz()</code> {.collection-method}$table->time('sunrise', $precision = 0);
timestampTz
方法创建一个具有可选精度(总位数)的 TIMESTAMP
(带时区)等效列:
<code>timestamp()</code> {.collection-method}$table->timestampTz('added_at', $precision = 0);
timestamp
方法创建一个具有可选精度(总位数)的 TIMESTAMP
等效列:
<code>timestampsTz()</code> {.collection-method}$table->timestamp('added_at', $precision = 0);
timestampsTz
方法创建 created_at
和 updated_at
TIMESTAMP
(带时区)等效列,具有可选精度(总位数):
<code>timestamps()</code> {.collection-method}$table->timestampsTz($precision = 0);
timestamps
方法创建具有可选精度(总位数)的 created_at
和 updated_at
TIMESTAMP
等效列:
<code>tinyIncrements()</code> {.collection-method}$table->timestamps($precision = 0);
tinyIncrements
方法创建一个自动递增的 UNSIGNED TINYINT
等效列作为主键:
<code>tinyInteger()</code> {.collection-method}$table->tinyIncrements('id');
tinyInteger
方法用于创建一个 TINYINT
等效列:
<code>tinyText()</code> {.collection-method}$table->tinyInteger('votes');
tinyText
方法用于创建一个 TINYTEXT
等效列:
<code>unsignedBigInteger()</code> {.collection-method}$table->tinyText('notes');
unsignedBigInteger
方法用于创建一个 UNSIGNED BIGINT
等效列:
<code>unsignedDecimal()</code> {.collection-method}$table->unsignedBigInteger('votes');
unsignedDecimal
方法用于创建一个 UNSIGNED DECIMAL
等效列,具有可选的精度(总位数)和小数位数(小数位数):
<code>unsignedInteger()</code> {.collection-method}$table->unsignedDecimal('amount', $precision = 8, $scale = 2);
unsignedInteger
方法用于创建一个 UNSIGNED INTEGER
等效列:
<code>unsignedMediumInteger()</code> {.collection-method}$table->unsignedInteger('votes');
unsignedMediumInteger
方法用于创建一个 UNSIGNED MEDIUMINT
等效列:
<code>unsignedSmallInteger()</code> {.collection-method}$table->unsignedMediumInteger('votes');
unsignedSmallInteger
方法用于创建一个 UNSIGNED SMALLINT
等效列:
<code>unsignedTinyInteger()</code> {.collection-method}$table->unsignedSmallInteger('votes');
unsignedTinyInteger
方法用于创建一个 UNSIGNED TINYINT
等效列:
<code>ulidMorphs()</code> {.collection-method}$table->unsignedTinyInteger('votes');
ulidMorphs
方法用于快速创建一个名称为 {column}_id
,类型为 CHAR(26)
的列和一个名称为 {column}_type
,类型为 VARCHAR
的列。
这个方法用于定义使用 UUID 标识符的多态关联所需的列时使用。在下面的例子中,taggable_id
和 taggable_type
这两个列将会被创建:
<code>uuidMorphs()</code> {.collection-method}$table->ulidMorphs('taggable');
uuidMorphs
方法用于快速创建一个名称为 {column}_id
,类型为 CHAR(36)
的列和一个名称为 {column}_type
,类型为 VARCHAR
的列。
这个方法用于定义使用 UUID 标识符的多态关联所需的列时使用。在下面的例子中,taggable_id
和 taggable_type
这两个列将会被创建:
<code>ulid()</code> {.collection-method}$table->uuidMorphs('taggable');
ulid
方法用于创建一个 ULID
类型的列:
<code>uuid()</code> {.collection-method}$table->ulid('id');
uuid
方法用于创建一个 UUID
类型的列:
<code>year()</code> {.collection-method}$table->uuid('id');
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() |
设置 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) |
->unsigned() |
设置 INTEGER 类型的字段为 UNSIGNED (MySQL) |
->useCurrent() |
设置 TIMESTAMP 类型的列使用 CURRENT_TIMESTAMP 作为默认值 |
->useCurrentOnUpdate() |
将 TIMESTAMP 类型的列设置为在更新时使用 CURRENT_TIMESTAMP 作为新值 |
->virtualAs($expression) |
创建一个虚拟生成的列 (MySQL) |
->generatedAs($expression) |
使用指定的序列选项创建标识列 (PostgreSQL) |
->always() |
定义序列值优先于标识列的输入 (PostgreSQL) |
->isGeometry() |
将空间列类型设置为 geometry - 默认类型为 geography (PostgreSQL)。 |
default
修饰符接收一个变量或者一个 \Illuminate\Database\Query\Expression
实例。使用 Expression
实例可以避免使用包含在引号中的值,并且允许你使用特定数据库函数。这在当你需要给 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
属性,你必须在修改列时明确每个属性的修改。
在 SQLite 上修改列Schema::table('users', function (Blueprint $table) { $table->integer('votes')->unsigned()->default(1)->comment('my comment')->change(); });
如果应用程序使用的是 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, ], ],
要重命名一个列,你可以使用模式构建器提供的 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() 方法别名 |
索引
创建索引
结构生成器支持多种类型的索引。下面的例子中新建了一个值唯一的 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) |
默认情况下,Laravel 使用 utf8mb4
编码。如果你是在版本低于 5.7.7 的 MySQL 或者版本低于 10.2.2 的 MariaDB 上创建索引,那你就需要手动配置数据库迁移的默认字符串长度。也就是说,你可以通过在 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')
删除索引
若要删除索引,则必须指定索引的名称。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) |
如果将字段数组传给 dropIndex
方法,会删除根据表名、字段和键类型生成的索引名称。
Schema::table('geo', function (Blueprint $table) { $table->dropIndex(['state']); // 删除 'geo_state_index' 索引 });
外键约束
Laravel 还支持创建用于在数据库层中的强制引用完整性的外键约束。例如,让我们在 posts
表上定义一个引用 users
表的 id
字段的 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
方法是 unsignedBigInteger
的别名,而 constrained
方法将使用约定来确定所引用的表名和列名。如果表名与约定不匹配,可以通过将表名作为参数传递给 constrained
方法来指定表名:
Schema::table('posts', function (Blueprint $table) { $table->foreignId('user_id')->constrained('users'); });
你可以为约束的「on delete」和「on update」属性指定所需的操作:
$table->foreignId('user_id') ->constrained() ->onUpdate('cascade') ->onDelete('cascade');
还为这些操作提供了另一种表达性语法:
方法 | 说明 |
---|---|
$table->cascadeOnUpdate(); |
更新应该级联 |
$table->restrictOnUpdate(); |
应该限制更新 |
$table->cascadeOnDelete(); |
删除应该级联 |
$table->restrictOnDelete(); |
应该限制删除 |
$table->nullOnDelete(); |
删除应将外键值设置为空 |
当使用任意 字段修饰符 的时候,必须在调用 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
类: