CakePHP3の便利なCollectionクラス。Collection化したオブジェクトはextract()やfilter()等の便利な関数を備えており、DBからselectしてきたデータの加工や抽出に一役買ってくれる。
今回はその中でもmax()関数とmin()関数の挙動についてメモを残しておく。検証したCakePHPのバージョンは3.6.13。
max(),min()関数の使いどころ
例えば
複数の記事レコードのうち、ページビュー数が最大のものを1件だけピックアップしたい
とする。
1 2 3 4 5 |
$maxPageViewArticle = $this->Articles ->find() ->order(['page_view_count' => 'DESC']) ->limit(1) ->first(); |
上記のようにMySQLのORDER BYとLIMIT 1を使う手もあるが、CollectionTraitをuseしているResultSetオブジェクトでも似たような表現ができる:
1 2 3 |
$maxPageViewArticle = $this->Articles ->find() ->max('page_view_count'); |
この書き方のほうがシンプルではあるが、where句である程度絞りこんでおかないとSQLが重くなりがちなので注意が必要。
max()関数を使った時にfirst()を呼ばなくても良い理由は、
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
// ... 省略 ... /** * {@inheritDoc} */ public function max($callback, $type = \SORT_NUMERIC) { return (new SortIterator($this->unwrap(), $callback, \SORT_DESC, $type))->first(); } /** * {@inheritDoc} */ public function min($callback, $type = \SORT_NUMERIC) { return (new SortIterator($this->unwrap(), $callback, \SORT_ASC, $type))->first(); } // ... 省略 ... |
そもそもmax(),min()関数共にfirst()関数を最後に呼び出すためである。覚えておこう。
最大値・最小値を2つ以上持つレコードがある時に、該当レコードを全て抽出したい時の書き方
最大値・最小値を複数持つレコードを全て抽出するためには、段階を踏んだコードを書く必要がある。やるならこんな感じ:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
// 使い回しやすいよう、Queryオブジェクトを生成 $query = $this->Articles ->find(); // max_view_countの値を先に取得しておく $maxViewCount = $query ->max('page_view_count') ->page_view_count; // 【A案】SQLに組み込んで抽出 $maxPageViewArticle = $query ->where(['page_view_count' => $maxViewCount]) ->toArray(); // 【B案】filter()関数で抽出 $maxPageViewArticle = $query ->filter(function ($article) use ($maxViewCount) { return $article->page_view_count === $maxViewCount; }) ->toArray(); |
よほどの特殊な理由がない限りはA案を推奨。B案はSQLが重くなりがちなので。