如何在Eloquent关系中对一个数据透视表列进行GROUP和SUM?

Tho*_*sen 9 relationships laravel eloquent laravel-4

在Laravel 4; 我有模型ProjectPart,他们有一个透视表许多一对多的关系project_part.数据透视表有一列count,其中包含项目中使用的部件ID的编号,例如:

id  project_id  part_id count
24  6           230     3
Run Code Online (Sandbox Code Playgroud)

这里的project_id6,是使用3件part_id230.

对于同一项目,可以多次列出一个部分,例如:

id  project_id  part_id count
24  6           230     3
92  6           230     1
Run Code Online (Sandbox Code Playgroud)

当我显示项目的零件清单时,我不想显示part_id两次,所以我将结果分组.

我的项目模型有:

public function parts()
{
    return $this->belongsToMany('Part', 'project_part', 'project_id', 'part_id')
         ->withPivot('count')
         ->withTimestamps()
         ->groupBy('pivot_part_id')
}
Run Code Online (Sandbox Code Playgroud)

但当然我的count价值不正确,这就是我的问题:如何获得项目所有分组部分的总和?

这意味着我的零件清单project_id应该如下所示:

part_id count
230     4
Run Code Online (Sandbox Code Playgroud)

我真的很想把它放在Projects- Parts关系中,所以我可以急于加载它.

如果没有得到N + 1问题,我无法解决如何做到这一点,任何见解都表示赞赏.


更新:作为临时解决方案,我创建了一个presenter方法来获取项目中的总计数.但这给了我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;
}
Run Code Online (Sandbox Code Playgroud)

chu*_*gbo 18

尝试总结Collection,

$project->parts->sum('pivot.count');
Run Code Online (Sandbox Code Playgroud)

这是我找到的最佳方式.它很干净(易于阅读),能够在parts多对多定义中重复使用所有范围,排序和关系属性缓存.

@hebron没有N + 1个问题该解决方案,如果您使用with('parts')渴望负荷.因为$project->parts(没有函数调用)是一个缓存属性,所以返回一个包含所有数据的Collection实例.并且sum('pivot.count')是一种Collection包含纯函数助手的方法(不相对于数据库,如js世界中的下划线).

完整示例:

关系部分的定义:

class Project extends Model
{
    public function parts()
    {
        return $this->belongsToMany('Part', 'project_part', 'project_id', 'part_id')
            ->withPivot('count')
            ->withTimestamps();
    }
}
Run Code Online (Sandbox Code Playgroud)

当你使用它时(注意,急切的负载对于避免N + 1问题很重要),

App\Project::with('parts')->get()->each(function ($project) {
    dump($project->parts->sum('pivot.count'));
});
Run Code Online (Sandbox Code Playgroud)

或者您可以在Project.php中定义sum函数,

class Project extends Model
{
    ...

    /**
     * Get parts count.
     *
     * @return integer
     */
    public function partsCount()
    {
        return $this->parts->sum('pivot.count');
    }
}
Run Code Online (Sandbox Code Playgroud)

如果要避免with('parts')在调用方(默认情况下是急切的加载部分),可以添加$with属性

class Project extends Model
{
    /**
     * The relations to eager load on every query.
     *
     * @var array
     */
    protected $with = ['parts'];

    ...
}
Run Code Online (Sandbox Code Playgroud)


Raz*_*zor 8

代码源:

我们需要使用"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')
}
Run Code Online (Sandbox Code Playgroud)