首页
/ Coolify 仓库中的 Laravel 最佳实践技能:19 类规则体系与源码印证指南

Coolify 仓库中的 Laravel 最佳实践技能:19 类规则体系与源码印证指南

2026-09-05 14:31:35作者:邬祺芯Juliet

Coolify 是一个基于 Laravel 构建的开源自托管 PaaS,其仓库中内置了一套完整的 AI 辅助开发技能文件 .agents/skills/laravel-best-practices/SKILL.md,系统性地总结了 19 大类 Laravel 后端最佳实践。读完本文,你将掌握这套按影响力排序的规则体系——包括数据库 N+1 治理、队列重试与锁机制、缓存分层策略、安全边界与任务调度的完整要点,并能看到 Coolify 仓库自身的配置与 Job 实现如何印证这些规则。

一、技能文件结构与"一致性优先"原则

这份技能文件的 frontmatter 声明了触发条件:凡涉及控制器、模型、迁移、Form Request、Policy、Job、定时命令、Service 类的编写、审查或重构,均适用该技能。其正文结构分为三部分:

  • Consistency First(一致性优先):应用任何规则之前,先检查代码库已有的做法。Laravel 往往存在多种合法写法——最佳选择是代码库已经采用的那一种,即使另一种模式理论上更优。"不一致性比次优模式更糟"。因此这些规则是"尚无可循模式时的默认值",而非推翻既有约定的强制覆盖。
  • Quick Reference(快速参考):19 类规则目录,每类都链接到 rules/ 目录下的详细规则文件,并附要点清单。
  • How to Apply(应用方法):按文件类型选取相关章节(如迁移 → §16、控制器 → §1/§3/§5/§6/§10)、先查兄弟文件的既有模式、再按已安装版本核对 API 语法。

下表是该快速参考目录的完整映射(已转换为仓库根目录相对路径):

编号 主题 规则文件
1 数据库性能 db-performance.md
2 高级查询模式 advanced-queries.md
3 安全 security.md
4 缓存 caching.md
5 Eloquent 模式 eloquent.md
6 验证与表单 validation.md
7 配置 config.md
8 测试模式 testing.md
9 队列与 Job queue-jobs.md
10 路由与控制器 routing.md
11 HTTP Client http-client.md
12 事件、通知与邮件 events-notifications.mdmail.md
13 错误处理 error-handling.md
14 任务调度 scheduling.md
15 架构 architecture.md
16 迁移 migrations.md
17 集合 collections.md
18 Blade 与视图 blade-views.md
19 约定与风格 style.md

二、数据库性能:N+1 治理与批量处理(§1)

db-performance.md 是 19 类规则中第一条,也是 Coolify 这类管理面板类项目最直接影响响应时间的领域。核心要点包括:

2.1 始终使用 with() 预加载关系

懒加载在循环中触发 N+1 查询(1 条主查询 + N 条关系查询)。正确写法是一次预加载,总共只执行 2 条查询:

// 错误(N+1,执行 1 + N 条查询)
$posts = Post::all();
foreach ($posts as $post) {
    echo $post->author->name;
}

// 正确(共 2 条查询)
$posts = Post::with('author')->get();
foreach ($posts as $post) {
    echo $post->author->name;
}

预加载时还可约束约束条件,只选择需要的列——务必包含外键列,否则关系无法匹配:

$users = User::with(['posts' => function ($query) {
    $query->select('id', 'user_id', 'title')
          ->where('published', true)
          ->latest()
          ->limit(10);
}])->get();

2.2 开发环境开启懒加载防护

AppServiceProvider::boot() 中启用以下代码,开发阶段访问未预加载关系时抛出 LazyLoadingViolationException,让 N+1 在开发期即被暴露:

public function boot(): void
{
    Model::preventLazyLoading(! app()->isProduction());
}

2.3 分块处理与游标迭代

  • 批量处理绝不 ::all() 加载数千条记录,用 chunk(200, ...)
  • 迭代中修改/删除记录时必须用 chunkById()——普通 chunk() 基于 OFFSET,行变动后会发生偏移漏处理:
User::where('active', false)->chunkById(200, function ($users) {
    $users->each->delete();
});
  • 只读大结果集迭代用 cursor()(PHP generator 逐条读取,内存高效);
  • 计数关系用 withCount() 而非加载整个集合再 ->count(),条件计数写法:
$posts = Post::withCount([
    'comments',
    'comments as approved_comments_count' => function ($query) {
        $query->where('approved', true);
    },
])->get();
  • Blade 模板中禁止执行查询——数据一律从控制器传入。

2.4 索引策略

对出现在 WHEREORDER BYJOINGROUP BY 中的列建索引;对常见查询模式(如 WHERE status = ? ORDER BY created_at)建复合索引。Coolify 自己的数据库迁移遵循同样思路,大量外键使用 foreignId(...)->constrained() 自动建索引,例如 2023_03_24_140711_create_servers_table.php

三、高级查询模式(§2)

advanced-queries.md 提供了 8 个进阶技巧,解决"预加载整个 has-many 只为取一个值"这类低效模式:

  • addSelect() 相关子查询:为 has-many 关系取单值(如最近登录时间)时,直接注入主查询,零额外查询,并可用 withCasts() 附带类型转换;
  • 子查询 FK + 虚拟 belongsTo:先用子查询取 last_login_id,再在模型上定义 belongsTo,获得完全水合的关联模型而非整个集合;
  • 条件聚合替代多次 countselectRaw("count(case when status = 'X' then 1 end)") 一条查询完成 N 个状态计数,toBase() 跳过模型水合;
  • setRelation() 打断循环 N+1:父模型已随子集合加载后,$children->each->setRelation('parent', $parent) 阻止 Eloquent 再发 N 条查询;
  • whereIn + 子查询优于 whereHaswhereHas 发出逐行重执行的关联 EXISTS 子查询,而 whereIn('company_id', Company::where(...)->select('id')) 允许数据库走索引查找;
  • 两条简单查询可胜一条复杂查询:当二级查询高选择性且有独立索引时,多一次往返是划算的;
  • 复合索引列序须与 ORDER BY 一致——单列索引无法组合用于多列排序,数据库只能 filesort;
  • has-many 排序用相关子查询而非 join(join 会复制行):
public function scopeOrderByLastLogin($query): void
{
    $query->orderByDesc(Login::select('created_at')
        ->whereColumn('user_id', 'users.id')
        ->latest()
        ->take(1)
    );
}

四、安全规则(§3)

security.md 覆盖了 9 个安全主题,每条都可直接作为代码评审检查项:

  1. 批量赋值防护:每个模型必须定义 $fillable$guarded;接受用户输入的模型禁用 $guarded = []
  2. 每次操作都授权:控制器中用 Gate::authorize('update', $post),或在 Form Request 的 authorize() 方法中 $this->user()->can('update', $this->route('post'))。Coolify 的 app/Policies/ 目录实现了 27 个 Policy 类(如 ApplicationPolicy.phpServerPolicy.php),正是该规则在真实项目中的落地形态;
  3. 防 SQL 注入:只用参数绑定,User::whereRaw('LOWER(name) = ?', [strtolower($request->name)])
  4. 防 XSS:输出用 {{ }}{!! !!} 仅限可信的、已净化的内容;
  5. CSRF:所有 POST/PUT/DELETE 的 Blade 表单包含 @csrf(Inertia 应用自动处理);
  6. 限流:认证与 API 路由套 throttleRateLimiter::for('login', fn (Request $request) => Limit::perMinute(5)->by($request->ip()))
  7. 文件上传校验mimes 查扩展名、mimetypes 查真实 MIME、max 限制大小,存储时用生成的文件名;
  8. 密钥管理.env 绝不入库,应用代码中只通过 config() 访问密钥,env() 仅允许出现在 config 文件中;
  9. 敏感字段加密:API key/token 用 encrypted cast 并标记 hidden,同时建议 CI 中定期执行 composer audit。Coolify 的 EncryptedArrayCast.php 与私钥加密迁移(2024_09_16_111428_encrypt_existing_private_keys.php)说明项目本身也在落实这一条。

五、缓存分层策略(§4)

caching.md 给出了一套从请求内到跨请求的缓存层次:

API 适用场景 关键行为
Cache::remember() 通用 cache-aside 替代手工 get/put 样板代码
Cache::flexible('key', [300, 600], fn) 高流量键的 stale-while-revalidate 5 分钟内新鲜,最长 10 分钟提供稍旧数据,后台刷新,避免缓存击穿时某用户必然慢响应
Cache::memo() 单请求内重复读同一键 内存驻留,5 次调用 = 1 次 Redis 往返
Cache tags 成组失效 Cache::tags(['user-1'])->flush();仅 redis/memcached/dynamodb 驱动支持
Cache::add() 原子条件写 仅当键不存在时写入,消除检查-写入竞态
once() 纯内存的每请求/每对象记忆化 完全不触碰缓存存储
Cache::lock() / lockForUpdate() 竞态条件 分布式锁
Failover store 生产环境 'failover' => ['driver' => 'failover', 'stores' => ['redis', 'database']],Redis 宕机自动降级

once()Cache::memo() 的选型口诀:只做昂贵计算的一次性缓存用 once();既想跨请求又想减少往返用 Cache::memo()

六、队列与 Job:Coolify 配置中的实证(§9)

queue-jobs.md 的规则与 Coolify 仓库的实际配置高度呼应,是本文最适合"规则—源码互证"的一节。

规则要点:

  • retry_after 必须大于 Job 的 timeout,否则 worker 会在任务仍在运行时重新派发,造成重复执行;重试间隔用指数退避 $backoff = [1, 5, 10]
  • ShouldBeUnique 防重复处理,uniqueId() 返回业务唯一键;ShouldBeUniqueUntilProcessing 则在处理开始时即释放锁,允许新实例入队;
  • 必须实现 failed() 显式处理终态失败,不能静默丢失;
  • 使用 retryUntil() 做时间上限重试时必须设 $tries = 0
  • 调用第三方 API 的 Job 套 RateLimited 中间件;相关 Job 用 Bus::batch() 整体成败。

Coolify 中的印证:

  • app/Jobs/CleanupHelperContainersJob.php 声明了 implements ShouldBeEncrypted, ShouldBeUnique, ShouldQueue——一个同时加密载荷、防重复执行的清理 Job,正是规则中 ShouldBeUnique 的典型应用场景;同类还有 CleanupInstanceStuffsJob.php 等;
  • config/queue.phpdatabasebeanstalkd 连接均为 retry_after => 90,而 redis 连接为 retry_after => 86400——不同驱动下该参数按最长 Job 运行时长取值,体现了"retry_after > 任何 timeout"的约束;
  • 项目启用了 Horizon:config/horizon.php 定义了 supervisor,maxProcessesHORIZON_MAX_PROCESSES(默认 4)控制并配有 balance/balanceMaxShift/balanceCooldown 自动扩缩参数,对应规则中"复杂多队列场景用 Horizon"的建议。

七、Eloquent、验证、路由:三条高频规则线(§5/§6/§10)

7.1 Eloquent 模式

eloquent.md 的要点:关系方法带正确返回类型提示(public function comments(): HasMany);可复用约束提取为 local scope(scopeActive),全局 scope 仅限软删除、多租户等普适约束且必须记录其存在;属性类型转换集中在 casts() 方法中;日期列必须 cast 为 datetime,模板直接用 Carbon 实例而非手工格式化;Post::whereBelongsTo($user) 替代手写下划线外键;查询中禁止硬编码表名字符串——必须用 (new User)->getTable(),迁移文件是唯一例外(迁移是冻结快照,引用日后可能被重命名的模型反而会坏掉)。

7.2 验证与表单

validation.md 五条规则:校验逻辑从控制器抽到 Form Request 类;新代码优先数组语法 ['required', 'email'](但先遵循项目既有风格);只允许 $request->validated(),绝不用 $request->all() 做批量赋值;条件校验用 Rule::when();跨字段自定义校验用 after() 方法返回闭包数组而非 withValidator()。Coolify 中对应的实践可见 app/Rules/ 下的 12 个自定义验证规则(如 DockerImageFormat.phpSafeWebhookUrl.php)。

7.3 路由与控制器

routing.md:隐式路由模型绑定(public function show(Post $post) 取代 findOrFail);嵌套资源用 ->scopeBindings() 强制父子从属;RESTful 端点用 Route::resource() / apiResource();控制器方法保持 10 行以内,业务逻辑抽到 Action/Service 类;Form Request 类型提示会在方法执行前自动触发验证与授权。Coolify 的路由层即按此组织:routes/api.phproutes/web.php 分别承载 API 与 Web 入口,控制器集中在 app/Http/Controllers/(48 个控制器文件)。

八、HTTP Client 与任务调度(§11/§14)

8.1 HTTP Client

http-client.md 对每个出站请求的要求:

  • 显式超时:默认 30 秒对多数 API 调用太长,Http::timeout(5)->connectTimeout(3) 快速失败;服务级客户端用 macro 固化配置;
  • 指数退避重试Http::retry([100, 500, 1000]),或仅对连接异常/5xx 重试;
  • 显式错误处理:HTTP Client 默认不抛 4xx/5xx,用 ->throw() 或手动检查 successful()/notFound()
  • Http::pool() 并发独立请求:三个接口并行发出而非串行;
  • 测试中 Http::fake() + preventStrayRequests():禁止真实出站,并覆盖 Http::failedConnection() 失败场景。

8.2 任务调度

scheduling.md 六条规则:时长不定的任务加 withoutOverlapping() 防止同任务双开;多服务器部署加 onOneServer()(依赖共享缓存驱动);runInBackground() 让同 tick 的慢任务不阻塞后续任务;environments(['production']) 防止生产专用任务(计费、报表)在 staging 误跑;takeUntilTimeout() 给处理无界游标的任务设时间上界;重复的 ->onOneServer()->timezone(...) 配置收敛到 Schedule::daily()->group(...) 调度分组。

九、其余规则要点与"应用方法"

Quick Reference 中还覆盖以下主题,各自在 rules/ 目录下有独立细则:

  • 配置(§7)env() 只出现在 config 文件中;环境判断用 App::environment() / app()->isProduction();文案走 config、语言包或常量,不硬编码。Coolify 的 config/ 目录含 30 余个配置文件(app.phpqueue.phphorizon.php 等),env() 调用均收敛其中;
  • 测试模式(§8)LazilyRefreshDatabase 优于 RefreshDatabaseassertModelExists()、工厂 state/sequence、fake 类(Event::fake() 等)须在工厂数据准备之后调用、recycle() 共享关联实例。Coolify 的测试体系位于 tests/(Pest 框架,Feature 测试 500+ 个文件);
  • 架构(§15):单一职责的 Action 类、依赖注入优先于 app() helper、默认 ORDER BY id DESCcreated_at DESC、UTF-8 安全用 mb_* 函数、defer() 处理响应后工作、Context 存请求级数据、Concurrency::run() 并行执行;
  • 迁移(§16)php artisan make:migration 生成、constrained() 建外键、绝不修改已上生产的迁移、索引随迁移加入而非事后补、列默认值与模型 $attributes 镜像一致、down() 默认可逆(故意不可逆的变更用前向修复迁移)、一个迁移只处理一件事——绝不混 DDL 与 DML;
  • 集合(§17):高阶消息处理简单集合操作、按是否需要关系在 cursor()lazy() 间选择、迭代中更新记录用 lazyById()、批量操作用 toQuery()
  • Blade 与视图(§18):组件模板中 $attributes->merge()、Blade 组件优于 @include@pushOnce 管理按组件脚本、View Composer 共享视图数据、@aware 传递深层组件 props;
  • 风格(§19):遵循 Laravel 命名约定、优先 Str/Arr/Number/Uri/Str::of() 等官方 helper 而非裸 PHP 函数、Blade 中不写 JS/CSS、PHP 类中不嵌 HTML。

应用方法(来自 SKILL.md 末尾):识别文件类型后只选取相关章节(迁移 → §16,控制器 → §1、§3、§5、§6、§10);先查兄弟文件的既有模式并按"一致性优先"遵循;最后按已安装的 Laravel 版本核对确切 API 语法。

十、结语:把规则体系落到代码库

这份技能文件的价值在于两点:一是按"影响力"而非字母序组织 19 类规则,每条都给出"做什么 + 为什么"的正反例;二是它明确自身定位为"无既有模式时的默认值",避免与真实代码库的既有约定冲突。对 Coolify 这样的 Laravel 项目而言,config/queue.php 中分驱动的 retry_after 设置、config/horizon.php 的 supervisor 自动扩缩、Job 类上的 ShouldBeUnique 实现,都是规则与工程实践相互印证的直接证据——读者可以沿文中路径对照仓库源码,把这套规则用作评审清单或重构依据。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
528
588
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
906
1.82 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
docsdocs
暂无描述
Markdown
891
5.78 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.53 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.34 K
1.45 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
987
504
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384