数据库 迁移
介绍
迁移就像是数据库的版本控制,允许你的团队定义和共享应用的数据库模式。如果你曾经不得不告诉团队成员在从源代码控制中拉取你的更改后手动向他们的本地数据库模式添加列,那么你就遇到了数据库迁移所解决的问题。
Laravel 的
Schema
facade
为所有支持的 Laravel 数据库系统提供创建和操作表的数据库无关支持。通常,迁移将使用这个 facade 来创建和修改数据库表和列。
生成迁移
你可以使用
make:migration
Artisan 命令
来生成一个数据库迁移。新的迁移将被放置在
database/migrations
目录中。每个迁移文件名都包含了时间戳,让 Laravel 能够确定迁移的顺序:
Laravel 会使用迁移名称来尝试猜测表的名称以及迁移是否会创建一个新表。如果 Laravel 能够从迁移名称中确定表名,Laravel 将预填充指定表的生成迁移文件。否则,你可以手动指定迁移文件中的表。
如果你想为生成的迁移指定一个自定义路径,执行
make:migration
命令时可以使用
--path
选项。给定的路径应与你的应用的基本路径相关。
NOTE
迁移模板可以使用 stub 发布 进行自定义。
压缩迁移
随着你构建应用,你可能会随着时间的积累越来越多的迁移。这可能导致你的
database/migrations
目录变得臃肿,可能包含数百个迁移。如果你愿意,你可以将你的迁移压缩成一个 SQL 文件。要开始,请执行
schema:dump
命令:
当你执行这个命令时,Laravel 会将一个 "模式" 文件写入你的应用的
database/schema
目录。模式文件的名称将对应于数据库连接。现在,当你尝试迁移你的数据库并且没有其他迁移被执行时,Laravel 将首先执行你正在使用的数据库连接的模式文件中的 SQL 语句。执行了模式文件的 SQL 语句后,Laravel 将执行未包含在模式转储中的任何剩余迁移。
如果你的应用的测试使用的是与你在本地开发期间通常使用的不同的数据库连接,则应该确保你已经使用该数据库连接转储了一个模式文件,以便你的测试能够构建你的数据库。在转储你在本地开发期间通常使用的数据库连接之后,你可能希望这样做:
你应该将数据库模式文件提交到源代码控制,以便其他新团队成员可以快速创建你的应用的初始数据库结构。
WARNING
迁移压缩仅适用于 MySQL、PostgreSQL 和 SQLite 数据库,并使用数据库的命令行客户端。
迁移结构
迁移类包含两个方法:
up
和
down
。
up
方法用于向数据库中添加新表、列或索引,而
down
方法应该撤销
up
方法执行的操作。
在这两个方法中,你可以使用 Laravel schema 构建器来表现地创建和修改表。要了解 schema 构建器上所有可用的方法,请查看
它的文档
。例如,以下迁移创建了一个
flights
表:
设置迁移连接
如果你的迁移将与应用的默认数据库连接以外的数据库连接进行交互,则应设置迁移的
$connection
属性:
运行迁移
要运行所有未完成的迁移,请执行
migrate
Artisan 命令:
如果你想要查看到目前为止已经运行的迁移,你可以使用
migrate:status
Artisan 命令:
如果你想看到迁移将执行的 SQL 语句而不实际运行它们,你可以为
migrate
命令提供
--pretend
标志:
隔离迁移执行
如果你正在多个服务器上部署应用并在部署过程中运行迁移,你可能不希望两个服务器同时尝试迁移数据库。为了避免这种情况,你可以在调用
migrate
命令时使用
isolated
选项。
当提供了
isolated
选项时,Laravel 会在试图运行迁移之前使用应用的缓存驱动获取一个原子锁。当该锁被持有时,所有其他尝试运行
migrate
命令的尝试都不会执行;然而,命令仍然会以成功的退出状态代码退出:
WARNING
要使用此功能,你的应用必须使用
memcached
、
redis
、
dynamodb
、
database
、
file
或
array
缓存驱动作为应用的默认缓存驱动。此外,所有服务器必须与相同的中央缓存服务器通信。
强制在生产环境中运行迁移
某些迁移操作是破坏性的,这意味着它们可能导致你丢失数据。为了防止你在生产数据库上运行这些命令,你将在执行命令前被提示确认。要强制命令运行而不提示,请使用
--force
标志:
回滚迁移
要回滚最近的迁移操作,你可以使用
rollback
Artisan 命令。此命令回滚最后一个“批次”的迁移,这可能包括多个迁移文件:
你可以通过为
rollback
命令提供
step
选项来回滚有限数量的迁移。例如,以下命令将回滚最后五个迁移:
你可以通过为
rollback
命令提供
batch
选项来回滚特定的“批次”迁移,其中
batch
选项对应于你的应用的
migrations
数据库表中的批次值。例如,以下命令将回滚批次三中的所有迁移:
如果你想看到迁移将执行的 SQL 语句而实际上并没有运行它们,你可以为
migrate:rollback
命令提供
--pretend
标志:
migrate:reset
命令将回滚你的应用的所有迁移:
使用单个命令回滚和迁移
migrate:refresh
命令将回滚所有的迁移然后执行
migrate
命令。这个命令有效地重新创建了你的整个数据库:
你可以通过为
refresh
命令提供
step
选项来回滚和重新迁移有限数量的迁移。例如,以下命令将回滚和重新迁移最后五个迁移:
删除所有表并迁移
migrate:fresh
命令将从数据库中删除所有表,然后执行
migrate
命令:
默认情况下,
migrate:fresh
命令只会删除默认数据库连接中的表。不过,你可以使用
--database
选项来指定应该迁移的数据库连接。数据库连接名称应该对应于你的应用的
database
配置文件
中定义的连接:
WARNING
无论它们的前缀是什么,
migrate:fresh
命令都会删除所有数据库表。在为其他应用共享的数据库上开发时,应谨慎使用此命令。
表
创建表
要创建新的数据库表,请在
Schema
facade 上使用
create
方法。
create
方法接受两个参数:第一个是表名,而第二个是一个闭包,该闭包会接收到一个
Blueprint
对象,可以用来定义新表:
创建表时,你可以使用 schema 构建器的 列方法 中的任何一个来定义表的列。
确定表 / 列的存在
你可以使用
hasTable
、
hasColumn
和
hasIndex
方法来判断一个表、列或索引是否存在:
数据库连接和表选项
如果你想在非应用默认连接的数据库连接上执行 schema 操作,使用
connection
方法:
此外,还有几个属性和方法可以用来定义表创建的其他方面。
engine
属性可用于在使用 MySQL 时指定表的存储引擎:
charset
和
collation
属性可用于在使用 MySQL 时指定创建表的字符集和排序规则:
temporary
方法可用于指示表应该是“临时的”。临时表只对当前连接的数据库会话可见,并且在连接关闭时会自动丢弃:
如果你想向数据库表添加一个"注释",你可以在表实例上调用
comment
方法。表注释目前只由 MySQL 和 PostgreSQL 支持:
更新表
Schema
facade 上的
table
方法可以用来更新现有表。与
create
方法类似,
table
方法接受两个参数:表名和一个接收
Blueprint
实例的闭包,你可以用来向表中添加列或索引:
重命名 / 删除表
要重命名现有数据库表,请使用
rename
方法:
要删除现有表,你可以使用
drop
或
dropIfExists
方法:
重命名带有外键的表
在重命名表之前,你应该验证表上的任何外键约束在你的迁移文件中是否具有明确的名称,而不是让 Laravel 分配一个基于约定的名称。否则,外键约束名称将引用旧的表名。
列
创建列
Schema
facade 上的
table
方法可以用来更新现有表。与
create
方法类似,
table
方法接受两个参数:表名和一个接收
Illuminate\Database\Schema\Blueprint
实例的闭包,你可以使用它向表中添加列:
可用的列类型
schema 构建器蓝图提供了各种方法,对应于你可以添加到数据库表的不同类型的列。所有可用的方法都列在下表中:
bigIncrements()
bigIncrements
方法创建一个自动增长的
UNSIGNED BIGINT
(主键)等效列:
bigInteger()
bigInteger
方法创建一个
BIGINT
等效列:
binary()
binary
方法创建一个
BLOB
等效列:
在使用 MySQL、MariaDB 或 SQL Server 时,你可以传递
length
和
fixed
参数来创建
VARBINARY
或
BINARY
等效列:
boolean()
boolean
方法创建一个
BOOLEAN
等效列:
char()
char
方法创建一个指定长度的
CHAR
等效列:
dateTimeTz()
dateTimeTz
方法创建一个带有可选小数秒精度的
DATETIME
(带时区)等效列:
dateTime()
dateTime
方法创建一个带有可选小数秒精度的
DATETIME
等效列:
date()
date
方法创建一个
DATE
等效列:
decimal()
decimal
方法创建一个具有指定精度(总位数)和比例(小数位数)的
DECIMAL
等效列:
double()
double
方法创建一个
DOUBLE
等效列:
enum()
enum
方法创建一个具有给定有效值的
ENUM
等效列:
float()
float
方法创建一个具有给定精度的
FLOAT
等效列:
foreignId()
foreignId
方法创建一个
UNSIGNED BIGINT
等效列:
foreignIdFor()
foreignIdFor
方法为给定的模型类添加一个
{column}_id
等效列。列类型将是
UNSIGNED BIGINT
、
CHAR(36)
或
CHAR(26)
,这取决于模型键类型:
foreignUlid()
foreignUlid
方法创建一个
ULID
等效列:
foreignUuid()
foreignUuid
方法创建一个
UUID
等效列:
geography()
geography
方法创建一个具有给定空间类型和 SRID(空间参考系统标识符)的
GEOGRAPHY
等效列:
NOTE
对于空间类型的支持取决于你的数据库驱动。请查阅你的数据库文档。如果你的应用使用 PostgreSQL 数据库,你必须在
geography
方法可用之前安装
PostGIS
扩展。
geometry()
geometry
方法创建一个具有给定空间类型和 SRID(空间参考系统标识符)的
GEOMETRY
等效列:
NOTE
对于空间类型的支持取决于你的数据库驱动。请查阅你的数据库文档。如果你的应用使用 PostgreSQL 数据库,你必须在
geometry
方法可用之前安装
PostGIS
扩展。
id()
id
方法是
bigIncrements
方法的别名。默认情况下,该方法将创建一个
id
列;但是,如果你想要为该列分配一个不同的名称,可以传递一个列名称:
increments()
increments
方法创建一个自动增长的
UNSIGNED INTEGER
等效列作为主键:
integer()
integer
方法创建一个
INTEGER
等效列:
ipAddress()
ipAddress
方法创建一个
VARCHAR
等效列:
使用 PostgreSQL 时,将创建一个
INET
列。
json()
json
方法创建一个等同于
JSON
的列:
jsonb()
jsonb
方法创建一个等同于
JSONB
的列:
longText()
longText
方法创建一个等同于
LONGTEXT
的列:
使用 MySQL 或 MariaDB 时,你可以应用
binary
字符集到该列,以创建等同于
LONGBLOB
的列:
macAddress()
macAddress
方法创建一个旨在存储 MAC 地址的列。某些数据库系统(如 PostgreSQL)具有用于此类数据的专用列类型。其他数据库系统将使用等同于字符串的列:
mediumIncrements()
mediumIncrements
方法创建一个自增的等同于
UNSIGNED MEDIUMINT
的主键列:
mediumInteger()
mediumInteger
方法创建一个等同于
MEDIUMINT
的列:
mediumText()
mediumText
方法创建一个等同于
MEDIUMTEXT
的列:
使用 MySQL 或 MariaDB 时,你可以应用
binary
字符集到该列,以创建等同于
MEDIUMBLOB
的列:
morphs()
morphs
方法是一个便利方法,添加一个
{column}_id
等同列和一个
{column}_type
等同于
VARCHAR
的列。
{column}_id
的列类型将根据模型键类型而定,可能是
UNSIGNED BIGINT
、
CHAR(36)
或
CHAR(26)
。
这个方法旨在用于定义多态
Eloquent 关系
所需的列。在以下示例中,将创建
taggable_id
和
taggable_type
列:
nullableTimestamps()
nullableTimestamps
方法是
timestamps
方法的别名:
nullableMorphs()
此方法类似于 morphs 方法;然而,创建的列将是“可为空”:
nullableUlidMorphs()
此方法类似于 ulidMorphs 方法;然而,创建的列将是“可为空”:
nullableUuidMorphs()
此方法类似于 uuidMorphs 方法;然而,创建的列将是“可为空”:
rememberToken()
rememberToken
方法创建一个可为空的
VARCHAR(100)
等同列,用于存储当前的 "记住我"
认证令牌
:
set()
set
方法创建一个
SET
等同列,并附有给定的有效值列表:
smallIncrements
smallIncrements
方法创建一个自增的
UNSIGNED SMALLINT
等效的列作为主键:
smallInteger
smallInteger
方法创建一个
SMALLINT
等效的列:
softDeletesTz
softDeletesTz
方法添加了一个可以为空的
deleted_at
TIMESTAMP
(带有时区)等效的列,可选的分数秒精度。此列旨在存储 Eloquent "软删除" 功能所需的
deleted_at
时间戳:
softDeletes
softDeletes
方法添加了一个可以为空的
deleted_at
TIMESTAMP
等效的列,可选的分数秒精度。此列旨在存储 Eloquent "软删除" 功能所需的
deleted_at
时间戳:
string
string
方法创建了一个给定长度的
VARCHAR
等效的列:
text
text
方法创建了一个
TEXT
等效的列:
在使用 MySQL 或 MariaDB 时,你可以将
binary
字符集应用于列,以创建一个
BLOB
等效的列:
timeTz
timeTz
方法创建了一个带有可选分数秒精度的
TIME
(带有时区)等效的列:
time
time
方法创建了一个带有可选分数秒精度的
TIME
等效的列:
timestampTz
timestampTz
方法创建了一个带有可选分数秒精度的
TIMESTAMP
(带有时区)等效的列:
timestamp
timestamp
方法创建了一个带有可选分数秒精度的
TIMESTAMP
等效的列:
timestampsTz
timestampsTz
方法创建了带有可选分数秒精度
created_at
和
updated_at
TIMESTAMP
(带有时区)等效的列:
timestamps
timestamps
方法创建了带有可选分数秒精度
created_at
和
updated_at
TIMESTAMP
等效的列:
tinyIncrements
tinyIncrements
方法创建了一个自增的
UNSIGNED TINYINT
等效的列作为主键:
tinyInteger
tinyInteger
方法创建了一个
TINYINT
等效的列:
tinyText
tinyText
方法创建了一个
TINYTEXT
等效的列:
在使用 MySQL 或 MariaDB 时,你可以将
binary
字符集应用于列,以创建一个
TINYBLOB
等效的列:
unsignedBigInteger
unsignedBigInteger
方法创建了一个
UNSIGNED BIGINT
等效的列:
unsignedInteger
unsignedInteger
方法创建了一个
UNSIGNED INTEGER
等效的列:
unsignedMediumInteger
unsignedMediumInteger
方法创建了一个
UNSIGNED MEDIUMINT
等效的列:
unsignedSmallInteger
unsignedSmallInteger
方法创建了一个
UNSIGNED SMALLINT
等效的列:
unsignedTinyInteger
unsignedTinyInteger
方法创建了一个
UNSIGNED TINYINT
等效的列:
ulidMorphs
ulidMorphs
方法是一个便捷方法,添加一个
{column}_id
CHAR(26)
等效列和一个
{column}_type
VARCHAR
等效列。
此方法旨在定义使用 ULID 标识符的多态
Eloquent 关系
所需的列时使用。在以下示例中,将创建
taggable_id
和
taggable_type
列:
uuidMorphs
uuidMorphs
方法是一个便捷方法,添加一个
{column}_id
CHAR(36)
等效列和一个
{column}_type
VARCHAR
等效列。
此方法旨在定义使用 UUID 标识符的多态
Eloquent 关系
所需的列时使用。在以下示例中,将创建
taggable_id
和
taggable_type
列:
ulid
ulid
方法创建一个
ULID
等效列:
uuid
uuid
方法创建一个
UUID
等效列:
vector
vector
方法创建一个
vector
等效列:
year
year
方法创建一个
YEAR
等效列:
列修饰符
除了上面列出的列类型之外,当给数据库表添加一个列时,还有几个你可以使用的列“修饰符”。例如,要使列可以“为空”,你可以使用
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 列分配默认值时,这是特别有用的情况:
:
默认表达式的支持取决于你的数据库驱动程序、数据库版本和字段类型。请参阅你的数据库文档。
列顺序
在使用 MySQL 数据库时,可以使用
after
方法在模式中的现有列之后添加列:
修改列
change
方法允许你修改现有列的类型和属性。例如,你可能希望增加
string
列的大小。要看到
change
方法的作用,让我们将
name
列的大小从 25 增加到 50。为此,我们只需定义列的新状态然后调用
change
方法:
修改列时,必须明确包括你想要保留的所有修饰符在列定义上 - 任何缺失的属性都将被丢弃。例如,要保留
unsigned
、
default
和
comment
属性,你必须在更改列时明确调用每个修饰符:
change
方法不改变列的索引。因此,你可以使用索引修饰符在修改列时明确添加或删除索引:
重命名列
要重命名列,可以使用模式构建器提供的
renameColumn
方法:
删除列
要删除列,可以在模式构建器上使用
dropColumn
方法:
可以通过将列名数组传递给
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
方法链到列定义上:
或者,你也可以在定义列之后创建索引。要做到这一点,你应该在模式构建器蓝图上调用
unique
方法。这个方法接受一个应该接收一个唯一索引的列的名称:
你甚至可以传递一个列数组到索引方法,以创建一个复合(或组合)索引:
创建索引时,Laravel 会自动根据表名,列名和索引类型生成一个索引名称,但你可以传递第二个参数给方法,自己指定索引名称:
可用的索引类型
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
方法。此方法接受当前索引名称作为其第一个参数,所需名称作为其第二个参数:
删除索引
要删除索引,必须指定索引的名称。默认情况下,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 除外)。 |
如果你传递一个列数组到删除索引的方法中,基于表名,列和索引类型的传统索引名称将被生成:
外键约束
Laravel 还支持创建外键约束,这些约束用于在数据库级别强制引用完整性。例如,让我们在
posts
表上定义一个
user_id
列,它引用
users
表上的
id
列:
由于这种语法相当冗长,Laravel 提供了额外的,简洁的方法,这些方法使用约定来提供更好的开发者体验。当使用
foreignId
方法创建列时,上面的例子可以重写如下:
foreignId
方法创建了一个
UNSIGNED BIGINT
等效的列,而
constrained
方法将利用约定确定正在被引用的表和列。如果你的表名不符合 Laravel 的约定,你可以手动将其提供给
constrained
方法。此外,还可以指定生成的索引应该分配的名称:
你还可以指定约束的 "on delete" 和 "on update" 属性的所需动作:
也提供了这些动作的另一种表现形式方法:
| 方法 | 描述 |
|---|---|
$table->cascadeOnUpdate();
|
更新应级联。 |
$table->restrictOnUpdate();
|
更新应受限。 |
$table->noActionOnUpdate();
|
更新不执行任何动作。 |
$table->cascadeOnDelete();
|
删除应级联。 |
$table->restrictOnDelete();
|
删除应受限。 |
$table->nullOnDelete();
|
删除应将外键值设为 null。 |
在
constrained
方法之前必须调用任何其他
列修饰符
:
切换外键约束
你可以通过使用以下方法在迁移中启用或禁用外键约束: