如何对 Eloquent 关系中的数据透视表列进行 GROUP 和 SUM?
Posted
技术标签:
【中文标题】如何对 Eloquent 关系中的数据透视表列进行 GROUP 和 SUM?【英文标题】:How to GROUP and SUM a pivot table column in Eloquent relationship? 【发布时间】:2015-03-31 19:08:33 【问题描述】:在 Laravel 4 中;我有模型Project
和Part
,它们与数据透视表project_part
具有多对多关系。数据透视表有一列 count
,其中包含项目中使用的部件 ID 的编号,例如:
id project_id part_id count
24 6 230 3
这里的project_id
6,使用了3个part_id
230。
同一项目的一个部分可能会被多次列出,例如:
id project_id part_id count
24 6 230 3
92 6 230 1
当我显示我的项目的零件清单时,我不想显示 part_id
两次,所以我将结果分组。
我的 Projects 模型有这个:
public function parts()
return $this->belongsToMany('Part', 'project_part', 'project_id', 'part_id')
->withPivot('count')
->withTimestamps()
->groupBy('pivot_part_id')
当然我的count
值不正确,我的问题来了:如何获得项目所有分组部分的总和?
意思是我的project_id
6 的零件清单应该是这样的:
part_id count
230 4
我真的很想把它放在Projects
-Parts
关系中,这样我就可以急切地加载它。
如果没有遇到 N+1 问题,我无法解决如何做到这一点,感谢任何见解。
更新: 作为临时解决方法,我创建了一个演示者方法来获取项目中的总零件数。但这给了我 N+1 问题。
public function sumPart($project_id)
$parts = DB::table('project_part')
->where('project_id', $project_id)
->where('part_id', $this->id)
->sum('count');
return $parts;
【问题讨论】:
【参考方案1】:来自code source:
我们需要使用“pivot_”前缀为所有枢轴列起别名,以便我们可以轻松地将它们从模型中提取出来,并在检索它们并将它们水合到模型中时将它们放入枢轴关系中。
所以你可以用select
方法做同样的事情
public function parts()
return $this->belongsToMany('Part', 'project_part', 'project_id', 'part_id')
->selectRaw('parts.*, sum(project_part.count) as pivot_count')
->withTimestamps()
->groupBy('project_part.pivot_part_id')
【讨论】:
这似乎有效,只是我必须将parts.*
也添加到selectRaw
,否则我没有得到任何其他字段。我会彻底测试它并让你知道。谢谢:)
我认为你不需要在幕后添加parts.*
,Eloquent add it。
虽然我也是这样,但如果不添加它,它就不是查询的一部分。
很奇怪,您实际上是如何构建查询的?你能分享执行的查询吗?你用什么版本?没有part.*
,你会得到什么(请转储)?
我正在使用 Laravel 4.2,我已经移动了一些东西。我在控制器中做selectRaw
,而不是在关系上,这样我可以选择是否要合并行。 pastebin.com/KTZRFigU这涵盖了吗?【参考方案2】:
尝试在Collection
中求和,
$project->parts->sum('pivot.count');
这是我找到的最好方法。它很干净(易于阅读),并且能够在 parts
多对多定义中重用所有范围、排序和关系属性缓存。
@hebron 如果您使用with('parts')
到eager load,则此解决方案没有N+1 问题。因为$project->parts
(没有函数调用)是一个缓存属性,所以返回一个包含所有数据的Collection 实例。而sum('pivot.count')
是Collection
的一个方法,它包含纯函数助手(与数据库无关,如js 世界中的underscore)。
完整示例:
关系部分的定义:
class Project extends Model
public function parts()
return $this->belongsToMany('Part', 'project_part', 'project_id', 'part_id')
->withPivot('count')
->withTimestamps();
当你使用它时(注意,Eager load 对于避免 N+1 问题很重要),
App\Project::with('parts')->get()->each(function ($project)
dump($project->parts->sum('pivot.count'));
);
或者你可以在Project.php中定义sum函数,
class Project extends Model
...
/**
* Get parts count.
*
* @return integer
*/
public function partsCount()
return $this->parts->sum('pivot.count');
如果您想在调用方避免with('parts')
(默认情况下急切加载部分),您可以添加$with
属性
class Project extends Model
/**
* The relations to eager load on every query.
*
* @var array
*/
protected $with = ['parts'];
...
【讨论】:
这不会给我 N+1 的问题吗? @hebron 此解决方案没有 N+1 个问题。示例已更新。【参考方案3】:您可以使用的最佳方法是:
$project->parts->sum('pivot.count');
我遇到了同样的问题,但这解决了我的问题。
【讨论】:
以上是关于如何对 Eloquent 关系中的数据透视表列进行 GROUP 和 SUM?的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Eloquent ORM 中按多对多关系的数据透视表的字段进行排序