Laravel Eloquent实现关联查询并按类型统计金额求和
解决Laravel Eloquent关联统计各类型金额总和的问题
Hey Andrew, 我来帮你搞定这个问题!你需要的是获取每个项目的全部字段,同时按发票类型分别统计金额总和,这可以通过条件聚合函数或者Laravel Eloquent自带的关联聚合方法来实现,下面给你两种可行的方案:
方案1:使用Query Builder的Join + 条件聚合
这种方式直接通过数据库查询构造器实现,适合不需要模型关联的场景:
$projects = DB::table('projects') ->leftJoin('invoices', 'projects.id', '=', 'invoices.proj_id') ->select([ 'projects.*', // 统计type为a的金额总和,别名a DB::raw('SUM(CASE WHEN invoices.type = "a" THEN invoices.amount ELSE 0 END) AS a'), // 统计type为b的金额总和,别名b DB::raw('SUM(CASE WHEN invoices.type = "b" THEN invoices.amount ELSE 0 END) AS b'), // 统计type为c的金额总和,别名c DB::raw('SUM(CASE WHEN invoices.type = "c" THEN invoices.amount ELSE 0 END) AS c') ]) ->groupBy('projects.id') // 按项目ID分组,确保每个项目只返回一条记录 ->get();
关键点说明:
leftJoin保证即使某个项目没有对应类型的发票,也会返回该项目的记录,对应类型的金额会显示0(如果去掉ELSE 0则会显示null)CASE WHEN是条件聚合的核心,只对匹配类型的发票金额求和- 如果你的MySQL开启了严格模式,需要把
projects表的所有字段都加到groupBy里,或者修改数据库配置中的sql_mode(不推荐),不过由于projects.id是主键,大部分MySQL版本会允许只按主键分组
方案2:使用Eloquent模型关联聚合(推荐)
如果你已经定义了Project和Invoice模型,并且建立了关联关系,这种方式更符合Laravel的最佳实践:
首先确保模型关联正确:
// app/Models/Project.php class Project extends Model { public function invoices() { return $this->hasMany(Invoice::class, 'proj_id'); } } // app/Models/Invoice.php class Invoice extends Model { public function project() { return $this->belongsTo(Project::class, 'proj_id'); } }
然后使用withSum方法分别统计各类型的金额:
$projects = Project::query() // 统计type为a的发票金额总和,别名a ->withSum(['invoices as a' => function ($query) { $query->where('type', 'a'); }], 'amount') // 统计type为b的发票金额总和,别名b ->withSum(['invoices as b' => function ($query) { $query->where('type', 'b'); }], 'amount') // 统计type为c的发票金额总和,别名c ->withSum(['invoices as c' => function ($query) { $query->where('type', 'c'); }], 'amount') ->get();
关键点说明:
withSum是Laravel提供的关联聚合方法,自动处理关联查询和聚合逻辑- 通过闭包过滤发票类型,并给每个聚合结果指定别名(
as a),最终结果中会直接出现a、b、c三个字段 - 这种方式不需要手动写
groupBy,Laravel会自动处理,代码更简洁易维护
两种方案最终都会得到你期望的结果:每个项目一行数据,包含项目全部字段和各类型发票的金额总和。
内容的提问来源于stack exchange,提问作者Andrew Good
相关产品推荐
相关产品推荐

