У меня три таблицы:
products: id|name|description|slug|category_id|... reviews: id|product_id|review_text|name|email|... review_rows id|review_id|criteria|rating
в обзорной таблице хранится текст обзора, автор обзора и имеет внешний ключ product_id. В таблице review_rows хранятся оценки для разных критериев, таких как:
---------------------------------------- | id | criteria | rating | review_id | ---------------------------------------- | 1 | price | 9 | 12 | ---------------------------------------- | 2 | service | 8 | 12 | ---------------------------------------- | 3 | price | 6 | 54 | ---------------------------------------- | 4 | service | 10 | 54 | ----------------------------------------
строки обзора связаны с таблицей обзора с внешним ключом review_id. Я установил свои модельные отношения следующим образом:
Product -> hasMany -> Review Review -> belongsTo -> Product Review -> hasMany -> ReviewRow ReviewRow -> belongsTo -> Review
Теперь я хотел бы отобразить средний рейтинг продукта на моей странице категории и продукта. Как я могу это достичь?
Мне нужно суммировать и усреднять все рецензии за просмотр, а затем суммировать и усреднять все из них для каждого обзора, чтобы получить общий рейтинг для этого продукта. Возможно ли это через Eloquent или мне нужно другое решение или другой дизайн / структура базы данных?
Заранее спасибо!
Вам нужно что-то подобное http://softonsofa.com/tweaking-eloquent-relations-how-to-get-hasmany-relation-count-efficiently/, только слегка скорректированное в соответствии с вашими потребностями:
public function reviewRows() { return $this->hasManyThrough('ReviewRow', 'Review'); } public function avgRating() { return $this->reviewRows() ->selectRaw('avg(rating) as aggregate, product_id') ->groupBy('product_id'); } public function getAvgRatingAttribute() { if ( ! array_key_exists('avgRating', $this->relations)) { $this->load('avgRating'); } $relation = $this->getRelation('avgRating')->first(); return ($relation) ? $relation->aggregate : null; }
Тогда просто:
// eager loading $products = Product::with('avgRating')->get(); $products->first()->avgRating; // '82.200' | null // lazy loading via dynamic property $product = Product::first() $product->avgRating; // '82.200' | null
Может быть, вы можете попробовать с отношениями Eloquent и немного помочь от php-функции array_reduce
//model/Reviews.php public function sum() { return array_reduce($this->hasMany('ReviewRows')->lists('rating'), "sumItems"); } public function sumItems ($carry, $item) { $carry += $item; return $carry; }
Или с запросами Eloquent RAW, такими как:
//model/Reviews.php public function avg() { $result = $this->hasMany('ReviewRows') ->select(DB::raw('avg(rating) average')) ->first(); return $result->average; }