从 1.x 升级到 2.x
将现有应用从 Laravel Feeds 1.x 迁移到 2.x 时,请按以下步骤操作。如果已安装的 1.x 版本已包含 Feed slug 或已更新的生成标题,请保留这些更改并跳过已完成的步骤。
运行环境要求
| 组件 | 1.x | 2.x |
|---|---|---|
| PHP | ^8.2 | ^8.4 |
| Laravel 组件 | ^11.0 || ^12.0 || ^13.0 | ^13.0 |
安装 2.x 之前,请升级应用的 Laravel 依赖,以及 Web 应用、CLI、调度器和队列进程使用的 PHP 运行环境。使用 PHP 8.2 或 8.3,或 Laravel 11.x 或 12.x 的应用必须继续使用 1.x。支持的环境请参阅兼容性。
升级前
- 保存当前应用代码、
composer.json、composer.lock和已发布的config/feeds.php。备份回滚所需的 Feed 注册表和生成文件。 - 记录注册 ID、标题、现有 slug、调度计划和输出位置。
- 查找部署脚本、cron 作业、手动调度、测试和应用代码中的
feed:generate调用。 - 生成有代表性的 Feed,并保留输出以便比较。
- 部署时,先暂停 Feed 调度,完成正在运行或已排队的 Feed 作业,再替换软件包并更改表结构。
应用更新
composer require dragon-code/laravel-feeds:^2.0 --with-all-dependencies
composer check-platform-reqs
将已安装软件包的 config/feeds.php 与应用已发布的副本进行比较,并保留应用专用设置。不要使用 --force 重新发布,因为它会覆盖该副本。
添加 Feed slug
PR #287 将选择单个 Feed 的方式从数据库 ID 改为通过 Str::slug(title) 生成的 slug。
如果软件包的 slug 迁移尚未执行,请发布迁移:
php artisan vendor:publish --tag=feeds-migrations
该命令会发布所有迁移文件。请验证已发布的文件并删除不需要的文件。
请设置新 add_slug_to_feeds_table 迁移文件名中的时间戳,使其位于应用实际的 create_feeds_table 迁移之后,并且位于所有 Feed 注册迁移之前。此顺序可确保 migrate:fresh 在写入注册记录之前添加 slug 列。然后运行迁移:
php artisan migrate
该迁移添加一个允许为空的 slug 列,并根据现有标题填充值,包括已软删除的记录。现有标题和 ID 会保留。
如果应用已有自行定义的 slug 列,且软件包迁移尚未执行,迁移会在回填任何数据之前失败。部署前请解决此表结构冲突;在保留数据并更新使用该列的代码之前,不要删除应用自己的列。
在 php artisan tinker 中查看已存储的值:
app(\DragonCode\LaravelFeed\Queries\FeedQuery::class)
->all()
->get(['id', 'title', 'slug']);
用于选择 Feed 的 slug 必须非空,并且在未删除的注册记录中保持唯一,包括未启用的记录。不同标题可能会规范化为相同的 slug。匹配到多个记录时,查询会抛出 MultipleRecordsFoundException;未知 slug 会抛出 FeedNotFoundException。请通过 Feed 模型更新标题,解决 slug 重复或为空的问题。
在 2.3.0 中,PR #334 将 fireModelEvent() 重写替换为 FeedObserver::saving()。之前会在 saving、creating 和 updating 之后从原始属性生成 slug,包括静默保存。现在使用 saving 观察者执行时经过访问器的标题;静默保存跳过观察者,后续监听器也不会触发重新计算。
PR #335 增加了 $feed->isDirty('slug') 检查:显式修改的 slug 会通过 Str::slug() 规范化,并在本次保存中保留,即使同时修改了 title。FeedQuery::create() 可以通过 extra: ['slug' => 'Custom Catalog'] 接受该值。未修改的 slug 仍根据 title 生成,因此后续普通保存可能会替换之前指定的自定义 slug。赋予规范化结果与当前 slug 相同的值不会阻止重新计算。
从较早的 1.x 或 2.x 版本升级时,请检查显式 slug 赋值、静默保存、标题访问器、事件监听器、部分字段查询、自定义类型转换和批量写入。测试存储的标题和 slug、后续普通保存、按 slug 查询及计划生成。仅安装更新不会重写现有行,但下一次普通保存可能改变自定义 slug 或通过访问器生成的 slug,即使标题未变。必要时更新命令和计划中写死的 slug。显式 slug、静默保存和监听器的处理方式见 Eloquent。
替换生成调用中的 ID
注册 ID 为 42、存储标题为 Products 的记录,迁移后的 slug 为 products。
更改前:
php artisan feed:generate 42
更改后:
php artisan feed:generate products
在 2.x 中,该参数始终是 slug;数字字符串不再用于选择数据库 ID。不带参数调用 php artisan feed:generate 时,仍会处理所有已启用的注册记录。使用 --target 时,只替换注册参数;目标键保持不变:
php artisan feed:generate products --target=42
以相同方式更新手动调度和代码中的命令调用。更改前:
use DragonCode\LaravelFeed\Commands\FeedGenerateCommand;
use Illuminate\Support\Facades\Schedule;
Schedule::command(FeedGenerateCommand::class, [42])->daily();
更改后:
use DragonCode\LaravelFeed\Commands\FeedGenerateCommand;
use Illuminate\Support\Facades\Schedule;
Schedule::command(FeedGenerateCommand::class, ['products'])->daily();
保留手动调度现有的防重叠、后台运行和执行频率设置。重命名注册记录时,请更新硬编码的 slug。ScheduleFeedHelper::register() 会自动使用已存储的 slug,并在标题或 cron 表达式更改时,让防重叠锁继续绑定到该注册记录。
FeedQuery::find(int $id)、delete(int $id) 和 restore(int $id) 仍使用 ID。按 slug 选择时,请使用 FeedQuery::findBySlug(string $slug)。参阅生成与调度。
检查生成的标题
PR #293 移除了 make:feed 写入新注册迁移或部署操作的标题中的 Feed 后缀。
运行 php artisan make:feed ProductsFeed 时:
| 值 | 更改前 | 更改后 |
|---|---|---|
| 生成的注册标题 | Products Feed | Products |
| 根据该标题生成的 slug | products-feed | products |
现有数据库标题和以前生成的注册文件不会被重写。请继续使用每条注册记录实际存储的 slug。这项标题更新不会更改 Feed 类名和输出文件名。
文档与软件包开发
PR #294 将文档页面从“机器学习”重命名为 AI 智能体。该页面现位于 /ai-agents/,重定向会保留指向旧 URL 的链接。
PR #297 确定了新的运行环境要求,并保留了 1.x 文档。请选择与已安装软件包匹配的文档版本;搜索结果和 LLM 上下文仅限于该版本。
软件包自身的开发环境升级到 Pest ^5.0 和 Testbench ^11.0。其 Pest 命令使用 --tia,在 CI、基准测试和快照更新中完整重新运行测试时还会使用 --fresh。这些是软件包的开发依赖,并非应用测试套件的要求。
验证
- 如果应用使用配置缓存,请重新构建缓存,并使用新代码重启队列进程和长期运行的调度器进程。
- 确认每条被选中的注册记录都有非空且唯一的 slug,并且命令和手动调度使用该 slug。
- 为每种已用格式生成一个 Feed;对照保存的
1.x输出,验证拆分文件数量、文件名和有代表性的输出。 - 检查实际使用的目标选择、自动调度、手动调度和队列生成。
- 运行覆盖自定义 Feed 模型、转换器、宏和预设子类的应用测试。
- 表结构和生成检查通过后,恢复 Feed 调度和队列处理。
回滚
再次暂停 Feed 生成并完成正在执行的作业。恢复以前的应用代码、composer.json、composer.lock、配置和生成命令,然后在兼容的运行环境中执行 composer install。
先确认恢复的 1.x 软件包是否已使用 slug,再应用该应用的迁移回滚策略。slug 迁移的 down() 方法只删除该列;它不会恢复已重命名的标题或生成文件。必要时,从备份恢复已更改的注册数据和输出文件。恢复生成之前,请重新构建配置缓存,并重启队列进程和调度器进程。