
Coolify 中的 Laravel Actions 实战多入口复用、队列生命周期控制与测试 Fake 完整指南【免费下载链接】coolifyAn open-source, self-hostable PaaS alternative to Vercel, Heroku Netlify that lets you easily deploy static sites, databases, full-stack applications and 280 one-click services on your own servers.项目地址: https://gitcode.com/GitHub_Trending/co/coolify本文以 Coolify 仓库中的技能文档 SKILL.md 为核心完整讲解如何基于lorisleiva/laravel-actions构建、重构与排障 Laravel Action 类从AsAction基础骨架、对象/控制器/队列/监听器/命令行五种入口模式到队列重试与去重的JobDecorator定制再到mock/spy/shouldRun等 Fake 测试策略。Coolify 在app/Actions下已有 57 个 Action 类作为真实参照读完你既能掌握文档给出的决策规则也能直接对照 Coolify 源码验证每一处调用关系。一、技能定位与快速工作流laravel-actionscomposer 约束为^2.10.2见 composer.json 第 34 行的核心价值是让同一段业务逻辑可以通过run、dispatch、路由、事件监听、artisan 命令多种入口调用并在测试中用 Fake 精确验证编排行为。Coolify 是这一模式的规模化实践者App\Actions命名空间下按领域划分子目录Application、Database、Proxy、Service、Server、Shared、Stripe、Development等例如 StartProxy、StartDatabaseProxy、CheckDomainDns 等。文档给出的六步标准工作流如下每一步都有明确的验证手段用composer show lorisleiva/laravel-actions确认包已安装创建或编辑 Action 类使用Lorisleiva\Actions\Concerns\AsActiontrait先实现handle(...)把核心业务逻辑放进去仅在需要对应入口时才添加适配器方法asController配合路由/可调用控制器asJob配合dispatchasListener配合事件监听器装配asCommand配合命令签名/描述为所选入口补充或更新测试测试需要隔离时使用 action fakeMyAction::fake()与断言MyAction::assertDispatched()。二、基础 Action 模式与项目约定最小骨架以下是最小可运行的 Action 类模板其余部分只在确有需要时扩展?php namespace App\Actions; use Lorisleiva\Actions\Concerns\AsAction; class PublishArticle { use AsAction; public function handle(int $articleId): bool { return true; } }项目约定ConventionsAction 类放在App\Actions下除非该领域已经存在子命名空间Coolify 即采用App\Actions\Database、App\Actions\Proxy这类领域子命名空间采用描述性的VerbNoun命名如PublishArticle、SyncVehicleTaxStatus。Coolify 中的实例包括StartProxy、StopDatabase、DeployServiceApplication、CleanupPreviewDeployment等均严格遵循动词 名词领域/业务逻辑全部留在handle(...)中传输层与框架相关逻辑HTTP、队列、控制台只允许出现在适配器方法asController、asJob、asListener、asCommand里所有 Action 方法的参数与返回值优先显式类型标注。Coolify 源码印证了这一点如 StartDatabaseProxy::handle 的入参使用了联合类型public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|StandaloneKeydb|StandaloneDragonfly|StandaloneClickhouse|ServiceDatabase $database)复杂数据契约如数组形状优先用 PHPDoc 说明而不是行内注释。何时用 Action、何时用普通 Service 类文档给出的决策规则很明确同一个用例需要多个入口HTTP、队列、事件、CLI或需要一等的编排/Fake 能力时用 Action逻辑是局部的、单一入口、且不太可能被当作 Action 复用时保留普通 Service 类即可。一个常见误区就是过度使用 Action——对一次性、单上下文的逻辑也套用 Action这被列入文档的 Common Pitfalls 之一。三、五种入口模式Entrypoint Patterns1. 以对象方式运行三种调用方式按推荐优先级排列首选使用 trait 提供的静态辅助方法PublishArticle::run($id)PublishArticle::make()-handle($id)依赖注入app(PublishArticle::class)-handle($id)。Coolify 源码中大量使用了静态辅助风格。以StartProxy为例同一个 Action 在代码库中同时存在异步与同步两种对象式调用Server.phpStartProxy::dispatch($this)—— 走队列异步执行ServerCheckJob.phpStartProxy::run($this-server, async: false)—— 在当前请求上下文中同步执行Livewire/Server/Navbar.phpStartProxy::run($this-server, force: true)—— 用户在 UI 上手动触发并强制重启。这种一个 Action、两种运行姿态正是 laravel-actions 设计目标在 Coolify 中的直接体现。2. 以控制器方式运行路由直接指向类invokable 风格Route::post(/articles/{id}/publish, PublishArticle::class)添加asController(...)做 HTTP 特定的适配并返回响应当输入来自 HTTP 时加入请求校验rules()或自定义校验钩子。从当前 Coolify 源码结构看app/Actions下的 57 个 Action 均未见asController的使用——Coolify 的 HTTP 层由 Laravel 控制器与 Livewire 组件承担Action 主要被控制器/Job 以对象方式调用。可以推断在本仓库新增控制器入口时应先评估是否真的需要asController而非机械套用。3. 以 Job 方式运行用PublishArticle::dispatch($id)派发asJob(...)仅用于队列特定行为领域逻辑保持在handle(...)中文档特别指出本项目的 Job 型 Action 常会额外定义队列生命周期方法与 Job 属性用于控制重试、唯一性与超时。项目模式带额外队列方法的 Job Action?php namespace App\Actions\Demo; use App\Models\Demo; use DateTime; use Lorisleiva\Actions\Concerns\AsAction; use Lorisleiva\Actions\Decorators\JobDecorator; class GetDemoData { use AsAction; public int $jobTries 3; public int $jobMaxExceptions 3; public function getJobRetryUntil(): DateTime { return now()-addMinutes(30); } public function getJobBackoff(): array { return [60, 120]; } public function getJobUniqueId(Demo $demo): string { return $demo-id; } public function handle(Demo $demo): void { // Core business logic. } public function asJob(JobDecorator $job, Demo $demo): void { // Queue-specific orchestration and retry behavior. $this-handle($demo); } }各成员按需使用含义如下$jobTries队列执行的最大尝试次数$jobMaxExceptions失败前允许的最大未处理异常数getJobRetryUntil()绝对重试截止时间getJobBackoff()每次重试的延迟策略getJobUniqueId(...)唯一 Job 的去重键asJob(JobDecorator $job, ...)访问尝试次数元数据做仅队列分支。Coolify 真实案例configureJob与队列路由StartDatabaseProxy 展示了队列定制的另一种高频用法——通过configureJob将 Job 路由到指定队列public function configureJob(JobDecorator $job): void { $job-onQueue(deployment_queue()); }其中deployment_queue()是 Coolify 定义于 bootstrap/helpers/shared.php 的辅助函数function deployment_queue(): string { return isCloud() ? deployments : high; }从源码注释看云端模式下部署类 Job 跑在专用deployments队列上由独立的 Horizon worker 池消费自托管模式则保留在共享的high队列上。路由由配置isCloud()决定而非环境变量因此派发进程无需特殊 env但云端的 worker 必须在HORIZON_QUEUES中包含deployments否则这些 Job 永远不会被消费——这是理解 Coolify 队列拓扑时容易被忽视的隐含前提。同样的configureJob模式还出现在 StartService 与 StartDatabase 中。参考文档 references/job.md 进一步列出了完整的方法面dispatchIf/dispatchUnless/dispatchSync/dispatchNow/dispatchAfterResponse等条件派发变体makeJob/makeUniqueJob/withChain与Bus::chain的链式编排以及测试断言assertPushed/assertNotPushed/assertPushedOn$callback接收 Action 实例、派发参数、JobDecorator实例与队列名四个参数。JobDecorator还提供getJobMiddleware、$jobConnection、$jobQueue、$jobTimeout、$jobRetryUntil、getJobDisplayName、getJobTags、getJobUniqueFor/$jobUniqueFor、getJobUniqueVia、getJobDeleteWhenMissingModels、jobFailed(?Throwable $e, ...$parameters)失败回调等成员重负载 Job 务必显式配置唯一性/超时/重试策略而不要依赖默认值。4. 以监听器方式运行在EventServiceProvider中把 Action 类注册为监听器使用asListener(EventName $event)内部委托给handle(...)。5. 以命令行方式运行定义$commandSignature与$commandDescription属性实现asCommand(Command $command)把控制台 IO 全部留在该方法内用use Illuminate\Console\Command;导入Command。四、测试策略两层矩阵与 AsFake 方法深度解析两层测试策略handle(...)测试验证业务正确性入口测试asController、asJob、asListener、asCommand验证装配/编排。文档推荐的 Action 测试矩阵业务规则测试用真实依赖/工厂直接调用handle(...)HTTP 装配测试命中路由/控制器用shouldRun或shouldNotRunfake 下游 ActionJob 装配测试以 Job 派发 Action断言预期的下游 Action 调用事件监听测试派发事件通过 fake/spy 断言 Action 交互控制台测试运行 artisan 命令断言 Action 调用与输出。AsFake 各方法的适用场景2.x按你想证明什么来有意识地选择方法mock()将 Action 替换为完整 mock。需要严格期望与参数断言时最佳。PublishArticle::mock() -shouldReceive(handle) -once() -with(42) -andReturnTrue();partialMock()替换为部分 mock。想保留大部分真实行为、只 stub 一个昂贵/内部方法时最佳。PublishArticle::partialMock() -shouldReceive(fetchRemoteData) -once() -andReturn([ok true]);spy()替换为 spy。适合执行后验证是否以 X 被调用无需预先定义全部期望。$spy PublishArticle::spy()-allows(handle)-andReturnTrue(); // execute code that triggers the action... $spy-shouldHaveReceived(handle)-with(42);shouldRun()mock()-shouldReceive(handle)的快捷方式适合紧凑的编排断言。PublishArticle::shouldRun()-once()-with(42)-andReturnTrue();shouldNotRun()mock()-shouldNotReceive(handle)的快捷方式适合守护子句测试与分支覆盖。PublishArticle::shouldNotRun();allowToRun()spy 放行handle的快捷方式。想让执行继续发生、同时验证交互时最佳。$spy PublishArticle::allowToRun()-andReturnTrue(); // ... $spy-shouldHaveReceived(handle)-once();isFake()与clearFake()isFake()检查该类当前是否已被替换clearFake()重置 fake防止跨测试泄漏。expect(PublishArticle::isFake())-toBeFalse(); PublishArticle::mock(); expect(PublishArticle::isFake())-toBeTrue(); PublishArticle::clearFake(); expect(PublishArticle::isFake())-toBeFalse();参考文档 references/testing-fakes.md 对上述方法逐一给出了更完整的示例含isFake的状态翻转流程可作为速查。实用默认值分支测试优先shouldRun()/shouldNotRun()可读性最好行为基本真实、只需验证调用时优先spy()/allowToRun()交互契约严格、需要快速失败时用mock()fake 可能泄漏到其他测试时在清理阶段调用clearFake()隔离副作用只 fake 被测边界的 Action不要 fake 一切。Pest 风格示例Coolify 的测试栈使用 Pest见 tests/Pest.php以下示例与项目风格一致it(dispatches the downstream action, function () { SendInvoiceEmail::shouldRun()-once()-withArgs(fn (int $invoiceId) $invoiceId 0); FinalizeInvoice::run(123); }); it(does not dispatch when invoice is already sent, function () { SendInvoiceEmail::shouldNotRun(); FinalizeInvoice::run(123, alreadySent: true); });运行最小相关测试集例如php artisan test --compact --filterPublishArticle或直接按具体测试文件运行。五、排障清单与常见陷阱排障清单Troubleshooting Checklist确认类使用了AsAction且命名空间与 autoload 匹配作为控制器使用时检查路由注册使用dispatch时检查队列配置Coolify 场景下即上文deployment_queue()的路由结果与 Horizon worker 的HORIZON_QUEUES配置在EventServiceProvider中核对事件到监听器的映射传输层关注点只放在适配器方法asController、asCommand等中而不是handle(...)。常见陷阱Common Pitfalls把 HTTP 响应/重定向逻辑写进handle(...)而不是asController(...)在多个as*方法中重复实现业务规则而不是委托给handle(...)在需要显式注册的地方误以为监听器装配会自动生效只测入口、跳过对handle(...)行为的直接测试对一次性、单上下文、无复用压力的逻辑过度使用 Action。六、分主题深入参考资料主文档刻意保持工作流 决策规则的聚焦按入口/主题拆分的深入参考位于同一技能目录下对象入口references/object.mdrun、make、runIf、runUnless及 DI 注入边界控制器入口references/controller.mdJob 入口references/job.mdmakeJob、withChain、assertPushed*及完整JobDecorator钩子清单监听器入口references/listener.md命令入口references/command.md属性Attributes用法references/with-attributes.md测试与 Fakereferences/testing-fakes.md排障references/troubleshooting.md。七、小结laravel-actions在 Coolify 中的落点是清晰可验证的57 个 Action 类统一收敛业务逻辑到handle(...)通过::run/::dispatch在同步与异步两种姿态间自由切换如StartProxy在 Server.php 中的双入口调用并通过configureJobdeployment_queue()把队列拓扑纳入 Action 自身的声明式配置。对读者而言最有操作价值的三条规则是业务逻辑只在handle写一次队列属性重试、退避、去重、队列名用JobDecorator成员显式声明测试按业务层handle直测 入口层 Fake 断言两层矩阵展开并默认优先shouldRun/shouldNotRun与spy/allowToRun。【免费下载链接】coolifyAn open-source, self-hostable PaaS alternative to Vercel, Heroku Netlify that lets you easily deploy static sites, databases, full-stack applications and 280 one-click services on your own servers.项目地址: https://gitcode.com/GitHub_Trending/co/coolify创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考