web-dev-qa-db-ja.com

Eloquent関係でピボットテーブル列をグループ化して合計する方法は?

Laravel 4;モデルProjectPartがあり、ピボットテーブルproject_partと多対多の関係があります。ピボットテーブルには、プロジェクトで使用されるパーツIDの番号を含む列countがあります。例:

id  project_id  part_id count
24  6           230     3

ここで、project_id 6は、3個のpart_id230を使用しています。

1つのパーツが同じプロジェクトに対して複数回リストされる場合があります。例:

id  project_id  part_id count
24  6           230     3
92  6           230     1

プロジェクトのパーツリストを表示するときに、part_idを2回表示したくないので、結果をグループ化します。

私のプロジェクトモデルにはこれがあります:

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

しかしもちろん、私のcount値は正しくありません。ここで、私の問題が発生します。プロジェクトのすべてのグループ化された部分の合計を取得するにはどうすればよいですか?

project_id6のパーツリストは次のようになります。

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;
}
10
Thomas Jensen

コードソース から:

すべてのピボット列に「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')
}
8
Razor

Collectionで合計してみてください、

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

これが私が見つけた最良の方法です。クリーンで(読みやすく)、parts多対多の定義で、スコープ、順序付け、および関係属性のキャッシュをすべて再利用できます。

@hebron with('parts') to eager load を使用する場合、このソリューションのN +1の問題はありません。 _$project->parts_(withoutfuntion call)はキャッシュされた属性であるため、すべてのデータを含む 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();
    }
}
_

あなたがそれを使うとき(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'];

    ...
}
_
18
chuangbo

使用できる最善の方法は次のとおりです。

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

私は同じ問題に直面しましたが、これで問題は解決しました。

0
Mahmoud