如何用Laravel Eloquent查询支付总额低于收费总额的发票
实现方案
前提准备
首先确保你的Invoices模型已经定义好对应的一对多关联:
// App\Models\Invoices.php public function charges() { return $this->hasMany(Charge::class, 'invoice_id', 'id'); } // 注意如果你的支付表模型名是Payment,关联名建议用复数形式更符合规范 public function payments() { return $this->hasMany(Payment::class, 'invoice_id', 'id'); }
如果你的支付表实际表名是payment而非Laravel默认的payments,记得在Payment模型中指定protected $table = 'payment';即可。
核心查询代码(Laravel 8+ 推荐写法)
直接用Eloquent原生的withSum方法实现,性能和可读性都最优,完全兼容Nova资源的自定义查询逻辑:
Invoices::query() // 统计对应 charge 总金额,别名 total_charge ->withSum('charges as total_charge', 'amount') // 统计对应 payment 总金额,别名 total_payment ->withSum('payments as total_payment', 'amount') // 筛选支付总金额小于账单总金额的发票,COALESCE 用于处理无关联数据时 null 转 0 ->havingRaw('COALESCE(total_payment, 0) < COALESCE(total_charge, 0)');
低版本Laravel兼容写法
如果你的Laravel版本低于8,不支持withSum方法,可以用join+聚合的方式实现:
Invoices::query() ->selectRaw('invoices.*, SUM(charges.amount) as total_charge, SUM(payments.amount) as total_payment') ->leftJoin('charges', 'charges.invoice_id', '=', 'invoices.id') ->leftJoin('payments', 'payments.invoice_id', '=', 'invoices.id') ->groupBy('invoices.id') ->havingRaw('COALESCE(total_payment, 0) < COALESCE(total_charge, 0)');
逻辑说明
- 用
havingRaw而非where是因为聚合计算得到的字段,必须走having子句筛选 COALESCE函数的作用是把没有关联数据时返回的null转为0,避免null参与比较时逻辑异常,比如某张发票没有任何支付记录时,总支付金额会按0计算- 上述链式调用可以直接放到Nova资源对应的
indexQuery等自定义查询方法中使用
内容的提问来源于stack exchange,提问作者yahya007
相关产品推荐
相关产品推荐

