ServiceProvider、VendorPublishCommand 和 Migrator。框架实现参考的是 v13.34.0。
复制给用户,还是从包中加载
publishesMigrations() 只是将复制源和复制目标注册为发布对象。即使启动提供者,也不会复制文件或执行 SQL。
另一方面,loadMigrationsFrom() 会向 Migrator 注册搜索路径。执行普通的 migrate 时,该路径下的文件也会成为执行对象,但仅启动提供者并不会执行它们。
如果设计上允许用户在执行前调整表名或列,发布方式是一个候选方案。如果由包来管理架构,且不预期用户编辑文件,也可以考虑直接加载方式。以下两个提供者示例是二选一的替代方案。
实现发布方式
添加包专属的标签,以便用户能够将其与其他资源区分开来发布。时间戳的变更与配置相关
官方文档说明,发布时会将迁移的时间戳更新为当前日期时间。但在ServiceProvider::publishesMigrations() 的实现中,只有当 database.migrations.update_date_on_publish 启用时,才会将复制源添加到时间戳更新对象中。获取该配置时的回退值为 false。
Laravel 13 标准应用程序的 config/database.php 中包含以下配置。对于沿用旧结构的应用程序,也请确认该配置是否存在。
VendorPublishCommand 会在路径与已注册复制源的实际路径一致、且复制目标的名称带有 YYYY_MM_DD_HHMMSS_ 格式时改写日期时间。它以命令开始时间为基准,每个目标文件依次增加 1 秒。如果名称中没有该格式,此处理不会附加日期时间。
时间戳更新同时依赖于包的注册和使用方应用程序的配置。请不要从包的提供者中统一修改该配置,而是在安装步骤中写明前提条件。如果使用了配置缓存,修改配置后还需要重新构建缓存。
重新发布并非“只添加未执行的部分”
vendor:publish 不会检查数据库的执行记录。此外,在 v13.34.0 的复制处理中,对已有文件的检查是针对时间戳变更前的复制目标进行的。即使是目录发布,也会先检查复制目标中是否存在与复制源相同的相对路径,然后才改写日期时间。
因此,如果首次发布时日期时间发生了变化,而应用程序中不存在与复制源同名的文件,那么再次发布同一标签时,可能会添加一个日期时间不同的文件。请不要认为只要不加 --force 就总能防止重复。
是否已执行由文件名判断
Migrator::getMigrationName() 返回去掉 .php 后的文件基本名。判断是否未执行时,会将该名称与执行记录进行比较。判断依据并不是 PHP 内容或表名是否相同。
实现直接加载方式
如果希望将包内的迁移直接作为执行对象,请注册搜索路径。在这种方式下,不要再添加发布相同文件的处理。loadMigrationsFrom() 会在 Migrator 被解析时调用 path()。Migrator::path() 会去除重复的搜索路径,getMigrationFiles() 则以迁移名称作为键存储找到的文件,并按名称排序。
用户更新包后,新文件会成为下一次 migrate 的执行对象。请不要更改已有文件的名称,而是为新的架构变更添加新文件。为避免与其他包冲突,请像 create_courier_deliveries_table 这样在名称中包含功能名。同名文件会成为同一个键,两者并不会各自独立执行。
将架构变更交付给现有用户
例如要为配送表添加追踪编号时,不要编辑已发布的create_courier_deliveries_table,而是添加一个用于变更的新文件。即使编辑已有的建表迁移,对于已经执行过的用户,这一变更也不会被执行。
database/migrations/2026_10_02_000000_add_tracking_code_to_courier_deliveries_table.php
发布前需要确认的事项
除了包的数据库测试之外,还请在使用方应用程序中确认发布和更新的步骤。仅在测试中直接加载迁移,并不等于验证了文件名会发生变化的发布方式。- 在空数据库上进行首次安装,能够创建所需的表。
- 从旧版本的数据库和执行记录进行更新时,只应用新的变更。
- 确认重复执行相同发布命令时的文件列表,更新步骤不会产生重复。
- 步骤已考虑日期时间更新的启用与禁用,以及对已发布文件的编辑。
- 确认新迁移的回滚,以及与应用程序中其他迁移的执行顺序。
相关页面
迁移
了解架构定义、执行记录和回滚的基础知识。
包测试
测试包的服务提供者和数据库。
包配置的合并与缓存
了解考虑用户配置和配置缓存的更新步骤。
版本兼容性管理
将更新步骤和兼容性变更与发布策略关联起来。