2015-02-01 7 views
4

В Laravel 4; У меня есть модель Project и Part, у них есть отношение «многие ко многим» с сводной таблицей project_part. Сводная таблица имеет столбец count, который содержит номер часть ID используемые в проекте, например:Как GROUP и SUM столбец столбцов в отношениях «Красноречивый»?

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, любое понимание будет оценено.


Update: В качестве временной работы вокруг я создал метод презентаторов, чтобы получить общее количество деталей в проекте. Но это дает мне вопрос 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; 
} 

ответ

5

От 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') 
} 
+0

Это, кажется, работает, только я должен был добавить 'части. *' В 'selectRaw 'также, иначе я не получил ни одного из других полей. Я тщательно проверю его и дам вам знать. Спасибо :) –

+0

Я не думаю, что вам нужно добавить 'parts. *', Eloquent [добавить его] (https://github.com/laravel/framework/blob/5.0/src/Illuminate/Database/Eloquent/Relations /BelongsToMany.php#L283) за кулисами. – Razor

+0

Это то, что я, хотя, но это не часть запроса, не добавляя его. –

6

Try просуммировать в Collection,

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

Это лучший способ я нашел. Он чист (легко читается) и способен повторно использовать все кэширование атрибутов scope, ordering и relationship в parts defination of many-to-many.

@hebron Нет проблемы с N + 1 для этого решения, если вы используете with('parts') до eager load.Потому что $project->parts (без funtion call) является кэшированным атрибутом, возвращает экземпляр Collection со всеми вашими данными. И sum('pivot.count') - это метод Collection, который содержит чистые funcional помощники (не относительно базы данных, например underscore в мире 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,

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']; 

    ... 
} 
+0

Не будет ли это проблемой N + 1? –

+0

@hebron No N + 1 проблема этого решения. Пример обновлен. – chuangbo

Смежные вопросы