ORMのコレクションを扱っていると、稀に複合的な条件でソートしたい場合があります。
調べてみると楽勝で実装できましたので、ご紹介いたします。
確認した環境
- PHP 7
- Laravel 5.3
ソートについて
並び替えをするコレクション
1$entities = collect([
2 ['id' => 1, 'name' => 'item1', 'price' => 117, 'modified' => 1479370620],
3 ['id' => 2, 'name' => 'item2', 'price' => 118, 'modified' => 1479370627],
4 ['id' => 3, 'name' => 'item3', 'price' => 117, 'modified' => 1479370617],
5]);
単一キーでのソート
ID昇順でソートする
sortBy()
を利用します。
1$ascSorted = $entities->sortBy('id');
ID降順でソートする
sortByDesc()
を利用します。
1$descSorted = $entities->sortByDesc('id');
複合キーでのソート
キーが1種類の場合はsortByを利用しますが、複合条件の場合はsort
を利用しクロージャの中でスコアを評価します。
昇順でソートする場合、返り値は次のルールで決定します。
-
- 等価の場合 0を返す
-
- (first < second) の場合 -1を返す
-
- (first > second) の場合 1を返す
降順の場合、上記の2と3の返り値が入れ替わりますよ。
[price昇順 > id降順]するサンプルを掲載します。
1$sortedEntities = $entities->sort(function($first, $second) {
2 if ($first->price == $second->price) {
3 return $first->id < $second->id ? 1 : -1 ;
4 }
5 return $first->price < $second->price ? -1 : 1 ;
6 });
7 // 引数が配列の場合は $first['id'] のように値を取り出してね
上記をdd()
した結果がこちらです。

コードについて、priceが等価である場合、第2ソートであるIDを評価する感じです。 どちらを-1にするのか迷ってしまいますが、ロジックは簡単ですね。
2017年6月22日追記
PHP7から導入された宇宙船演算子
を利用すればもっと直感的にコーディングすることができます。
1$sortedEntities = $entities->sort(function($first, $second) {
2 return $first->price <=> $second->price
3 ?: $second->id <=> $first->id;
4 });
スキーマビルダーでのソート
コレクション
とスキーマビルダー
は似ているものもありますが、もちろん別のもです。
Modelでのあらかじめソートしまう方がベターな場合はorderBy()
を使ってソートしましょう。
単一のソートはこう。
1$sortedById = Users::orderBy('id', 'desc')->get();
複合キーでのソートはこうです。
1$sortedById = Products::orderBy('price', 'acs')
2 ->orderBy('id', 'desc')
3 ->get();
スキーマビルダーの方が直感的ですね。
EXPLAINしてファイルソートが出ていないかはチェックしてください。
DBに問い合わせを実行するまでがスキーマビルダー、DB問い合わせ後はコレクションですが、時々混乱してしまいますね。
おわりに
面倒でわかりにくいphp標準関数のarray_multisort()
はもう使わなくていいんです。
参考にしたstackoverflowでは、汎用的な関数が実装されておりますので、ぜひご覧になってください。
下記にリンクを掲載します。
参考
Laravel 5.3 コレクション
https://readouble.com/laravel/5.3/ja/collections.html
php - What is the syntax for sorting an Eloquent collection by multiple columns?
http://stackoverflow.com/questions/25451019/what-is-the-syntax-for-sorting-an-eloquent-collection-by-multiple-columns