
本文详解如何在 laravel 9 中使用 eloquent 或 query builder 对子表(如 coletes)按父表 id(如 invoice_id)分组求和,避免全局求和错误,并生成结构化视图数据。
本文详解如何在 laravel 9 中使用 eloquent 或 query builder 对子表(如 coletes)按父表 id(如 invoice_id)分组求和,避免全局求和错误,并生成结构化视图数据。
在 Laravel 9 开发中,当需要为每张发票(invoices)汇总其关联的多个收款单(coletes)的 totaleuro 字段时,常见误区是直接使用 SUM() 而忽略分组逻辑,导致所有子记录被合并为一个总和(如 1700),而非按发票分别计算(700、1000)。根本原因在于 SQL 中缺少 GROUP BY 子句。
✅ 正确的查询写法(Query Builder)
需显式按 coletes.invoice_id 分组,并关联 invoices 表以确保数据完整性:
$invoiceTotals = DB::table('coletes')
->join('invoices', 'coletes.invoice_id', '=', 'invoices.id')
->select(
'invoices.id as invoice_id',
DB::raw('SUM(coletes.totaleuro) as total_euro')
)
->groupBy('coletes.invoice_id', 'invoices.id') // 推荐同时包含主键,避免 strict mode 报错
->orderBy('invoices.id')
->get();
执行后将返回集合:
[
{ "invoice_id" => 1, "total_euro" => 700 },
{ "invoice_id" => 2, "total_euro" => 1000 }
]
? 在 Blade 视图中渲染结构化表格
结合 Eloquent 关系(推荐方式),可更清晰地组织数据。首先在 Invoice 模型中定义关系:
// app/Models/Invoice.php
public function coletes()
{
return $this->hasMany(Colet::class, 'invoice_id');
}
控制器中预加载并计算子项总和:
$invoices = Invoice::with('coletes')
->orderBy('id')
->get()
->map(function ($invoice) {
$invoice->coleteTotal = $invoice->coletes->sum('totaleuro');
return $invoice;
});
Blade 模板(resources/views/invoices/index.blade.php)示例:
| Invoice ID | Colet ID | Total Euro |
|---|---|---|
| {{ $loop->first ? $invoice->id : '' }} | {{ $colet->id }} | {{ number_format($colet->totaleuro, 2) }} |
| Total | {{ number_format($invoice->coleteTotal, 2) }} | |
⚠️ 注意事项与最佳实践
-
SQL 严格模式兼容性:MySQL 8.0+ 默认启用
ONLY_FULL_GROUP_BY,因此GROUP BY必须包含所有非聚合字段(如invoices.id),否则报错; -
性能优化:对
coletes.invoice_id建立索引可显著提升 JOIN 和 GROUP BY 效率; -
空发票处理:若某发票无 coletes 记录,上述
JOIN查询将不返回该发票;如需包含空发票,改用leftJoin()并配合COALESCE(SUM(...), 0); -
Eloquent 替代方案:也可使用
Invoice::withSum('coletes', 'totaleuro')(Laravel 9.21+),自动注入coletes_sum_totaleuro属性,更简洁安全。
通过合理使用 GROUP BY 与模型关系,即可精准实现按父级 ID 分组聚合子表数据,构建清晰、可维护的财务汇总视图。











