Skip to main content
要持续维护使用数据库的包,不仅需要考虑首次安装,还需要一套将变更交付给已经存在数据表的用户的流程。请将迁移的发布、执行和执行记录作为彼此独立的处理来设计。 本页以包开发基础为前提,解读 Laravel 13 的 ServiceProvider、VendorPublishCommand 和 Migrator。框架实现参考的是 v13.34.0。

复制给用户,还是从包中加载

publishesMigrations() 只是将复制源和复制目标注册为发布对象。即使启动提供者,也不会复制文件或执行 SQL。 另一方面,loadMigrationsFrom() 会向 Migrator 注册搜索路径。执行普通的 migrate 时,该路径下的文件也会成为执行对象,但仅启动提供者并不会执行它们。 如果设计上允许用户在执行前调整表名或列,发布方式是一个候选方案。如果由包来管理架构,且不预期用户编辑文件,也可以考虑直接加载方式。以下两个提供者示例是二选一的替代方案。
请避免既发布同一个迁移又让其被直接加载的设计。如果发布时时间戳发生变化,复制源和复制目标会被视为不同的执行记录,同一个建表处理可能会被执行两次。

实现发布方式

添加包专属的标签,以便用户能够将其与其他资源区分开来发布。
首次安装时,指定目标提供者和标签进行复制,确认内容后再执行。如果同时指定两者,Laravel 会选择属于该提供者且带有该标签的发布对象。

时间戳的变更与配置相关

官方文档说明,发布时会将迁移的时间戳更新为当前日期时间。但在 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 内容或表名是否相同。
这两个是不同的迁移名称。即使前者已执行,仅凭该记录,后者也不会被视为已执行。
请不要在每次更新包时都无条件地重新执行首次安装用的发布命令,也不要将 --force 作为标准步骤。这不仅会覆盖对已发布文件的编辑,还可能因日期时间变更而添加重复的处理。

实现直接加载方式

如果希望将包内的迁移直接作为执行对象,请注册搜索路径。在这种方式下,不要再添加发布相同文件的处理。
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
由于这是向已有数据行的表中添加列的示例,这里将其设为 nullable。如果需要设为必填或回填数据,请另行设计相应步骤和执行顺序。 在发布方式下,请准备一套与用户已发布文件进行比对、只交付本次新增文件的更新步骤。也可以为新文件设置专用的发布标签,但如果启用了日期时间更新,反复执行该标签时同样需要注意。不要把更新步骤设计成仅仅重新执行首次安装用的标签。 在直接加载方式下,更新后的代码可以检测到新文件。无论采用哪种方式,仅仅存在文件并不会改变数据库,因此请在发布说明中明确写出需要执行迁移。

发布前需要确认的事项

除了包的数据库测试之外,还请在使用方应用程序中确认发布和更新的步骤。仅在测试中直接加载迁移,并不等于验证了文件名会发生变化的发布方式。
  • 在空数据库上进行首次安装,能够创建所需的表。
  • 从旧版本的数据库和执行记录进行更新时,只应用新的变更。
  • 确认重复执行相同发布命令时的文件列表,更新步骤不会产生重复。
  • 步骤已考虑日期时间更新的启用与禁用,以及对已发布文件的编辑。
  • 确认新迁移的回滚,以及与应用程序中其他迁移的执行顺序。

相关页面

迁移

了解架构定义、执行记录和回滚的基础知识。

包测试

测试包的服务提供者和数据库。

包配置的合并与缓存

了解考虑用户配置和配置缓存的更新步骤。

版本兼容性管理

将更新步骤和兼容性变更与发布策略关联起来。

参考的一手资料

最后修改于 2026年10月2日