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

时间:2015-02-01 20:35:36

标签: laravel laravel-4 eloquent relationships

在Laravel 4中;我有模型ProjectPart,它们与数据透视表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,所以我将结果分组。

我的项目模型有:

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问题,我无法理解如何做到这一点,我们非常感谢任何见解。


更新:作为临时解决方法,我创建了一个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;
}

3 个答案:

答案 0 :(得分:13)

尝试在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();
    }
}

当你使用它时(注意,急切的负载对于避免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'];

    ...
}

答案 1 :(得分:8)

来自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')
}

答案 2 :(得分:0)

最好的使用方式是:

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

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