Laravel Eloquent 实现按卡片颜色分组统计风险数量

1次阅读

Laravel Eloquent 实现按卡片颜色分组统计风险数量

本文详解如何使用 laravel eloquent 结合 join 与 group by,对关联模型(risks → cards)按 `cards.color` 字段分组计数,生成各颜色对应的风险数量统计结果。

laravel 应用中,当需要跨模型聚合统计(如“每种卡片颜色下有多少个风险”),直接使用 Eloquent 关系方法(如 withcount())可能无法满足按父表字段(如 cards.color)分组的需求。此时应结合查询构建器的 join()、select()、groupBy() 及 DB::raw() 实现精准聚合。

正确做法是:*从 risks 表出发,LEFT JOIN 或 INNER JOIN cards 表,选择 cards.color 并按其分组,再使用 `COUNT()` 统计每组风险数**。注意原答案中存在两处关键错误:

  • 表别名误写为 card(应为 cards);
  • SELECT 中引用了不存在的 card.name(cards 表无 name 字段,且需求只需 color);
  • 使用 rightJoin 易导致未关联卡片的风险被遗漏,推荐 leftJoin 或更稳妥的 innerJoin(仅统计有有效卡片的风险)。

✅ 推荐写法(使用 innerJoin,语义清晰、性能良好):

use IlluminateSupportFacadesDB; use AppModelsRisk;  $result = Risk::select('cards.color', DB::raw('count(*) as count'))     ->join('cards', 'risks.card_id', '=', 'cards.id')     ->groupBy('cards.color')     ->get();

执行后将返回如下集合(Eloquent Collection):

[   {"color": "green", "count": 4},   {"color": "blue",  "count": 2},   {"color": "red",   "count": 6},   {"color": "yellow","count": 1} ]

? 进阶建议:

  • 若需包含 cards.color 为 NULL 的风险(即 card_id 无效),改用 leftJoin 并添加 ->whereNotNULL(‘cards.id’) 控制逻辑;
  • 封装为 Risk 模型的 本地作用域(Local Scope) 提升复用性:
// In app/Models/Risk.php public function scopeWithColorCount($query) {     return $query->select('cards.color', DB::raw('count(*) as count'))         ->join('cards', 'risks.card_id', '=', 'cards.id')         ->groupBy('cards.color'); }  // 使用 $counts = Risk::withColorCount()->get();

⚠️ 注意事项:

  • 确保数据库中 cards.id 和 risks.card_id 字段类型一致(如均为 BIGINT UNSIGNED),避免隐式转换导致 JOIN 失效;
  • 若 cards.color 允许为 NULL,且需单独统计,可追加 ->orderByRaw(‘color IS NULL, color’) 保证排序可控;
  • 在高并发大数据量场景下,建议为 risks.card_id 添加索引以加速 JOIN 与 GROUP BY。

通过以上方式,即可在 Laravel 中优雅、高效地完成跨模型分组计数,无需脱离 Eloquent 生态直写原生 sql

text=ZqhQzanResources