はじめに
環境はCakePHP2.5.6
hogeに対して複数のreviewを持つ可能性があるという構成
Hoge hasMany Review
reviewsテーブルはid,score,content(レビュー内容),hoge_idという定義。
hogeをレビュー評価(平均)順にするには、どうしたらいいか……?
というものです。
解決策
「DBに新カラム?やめといて」と言われてしまいました。
正直それが一番いけそうだと思ったのですが……。ぐぇぇ
「VirtualFieldで出来るんじゃない?」なんていう助言も頂いたのですが、どうにも分かりませんでした。
最終的に辿り着いたのは、
① SQL文で綺麗にソートしたものを取得する
② 'order'にidを文字列で渡して、その順番で表示してもらう
という方法です。
調べまくっていて他にやっている人を見かけなかったので、ここに報告させて頂きます。
SQL文を好きなだけ使える上に、好き放題paginateも使えるんじゃないかと思います。
CakePHPの新たな可能性を見た、という気持ちです。
ソースコード
Model
Model/Hoge.php
public function getHogeSortScore() {
$query = "
SELECT
hoges.id,
hoges.name,
(sum(reviews.score))/count(review.id) as review_ave,
count(review.id) as review_count
FROM
hoges
LEFT JOIN reviews
ON (hoges.id = reviews.hoge_id)
group by hoges.id
order by review_ave desc, id;
";
$data = $this->query($query);
return $data
}
Controller
Controller/HogeController.php
public function index() {
$score_sort_hoges = $this->Hoge->getHogeSortScore();
$hoge_ids = array();
foreach ($score_sort_hoges as $item) {
// ただidだけの配列を作る
$hoge_ids[] = $item['hoges']['id'];
}
// 配列に入ったものを,で区切った文字列に
$str_hoge_ids = implode(',' , $hoge_ids);
// 指定した順番に表示してもらうため
$sort_order = 'FIELD(Hoge.id, ' . $str_hoge_ids . ')';
$this->paginate = array(
// 'conditions' => $conditions,
'limit' => 10,
'order' => $sort_order,
);
$options = array(
'Hoge.del_flg' => 0,
);
$hoges = $this->paginate($options);
$this->set(compact('hoges'));
}
修正
order byで、idも指定しておかないと
「1ページ目で出たものが2ページ目でも出てくる」なんていうヤバい表示になっていました。