如何对 Eloquent 关系中的数据透视表 table 列进行 GROUP 和 SUM?

How to GROUP and SUM a pivot table column in Eloquent relationship?

在Laravel 4;我有模型 ProjectPart,它们与枢轴 table project_part 存在多对多关系。数据透视表 table 有一列 count,其中包含项目中使用的部件 ID 的编号,例如:

id  project_id  part_id count
24  6           230     3

这里的project_id6,用的是3张part_id230.

一个部分可能会在同一个项目中多次列出,例如:

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;
}

来自code source

We need to alias all of the pivot columns with the "pivot_" prefix so we can easily extract them out of the models and put them into the pivot relationships when they are retrieved and hydrated into the models.

所以你可以用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')
}

尝试求和 Collection,

$project->parts->sum('pivot.count');

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

@hebron 如果您在 js 世界中使用 with('parts')eager load. Because $project->parts (without funtion call) is a cached attribute, return a instance of Collection with all your data. And sum('pivot.count') is a method of Collection which contains pure funcional helpers (not relative to database, like underscore,则此解决方案没有 N+1 问题。

完整示例:

关系部分定义:

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,

中定义求和函数
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'];

    ...
}

您可以使用的最佳方法是:

$project->parts->sum('pivot.count');

我遇到了同样的问题,但这解决了我的问题。