ORMのコレクションを扱っていると、稀に複合的な条件でソートしたい場合があります。
調べてみると楽勝で実装できましたので、ご紹介いたします。

確認した環境

  • PHP 7
  • Laravel 5.3

ソートについて

並び替えをするコレクション

$entities = collect([
  ['id' => 1, 'name' => 'item1', 'price' => 117, 'modified' => 1479370620],
  ['id' => 2, 'name' => 'item2', 'price' => 118, 'modified' => 1479370627],
  ['id' => 3, 'name' => 'item3', 'price' => 117, 'modified' => 1479370617],
]);

単一キーでのソート

ID昇順でソートする

sortBy()を利用します。

$ascSorted = $entities->sortBy('id');

ID降順でソートする

sortByDesc()を利用します。

$descSorted = $entities->sortByDesc('id');

複合キーでのソート

キーが1種類の場合はsortByを利用しますが、複合条件の場合はsortを利用しクロージャの中でスコアを評価します。

昇順でソートする場合、返り値は次のルールで決定します。

  • 1. 等価の場合 0を返す
  • 2. (first < second) の場合 -1を返す
  • 3. (first > second) の場合 1を返す

降順の場合、上記の2と3の返り値が入れ替わりますよ。


[price昇順 > id降順]するサンプルを掲載します。

$sortedEntities = $entities->sort(function($first, $second) {
     if ($first->price == $second->price) {
        return $first->id < $second->id ? 1 : -1 ;
     }
    return $first->price < $second->price ? -1 : 1 ;
  });
  // 引数が配列の場合は $first['id'] のように値を取り出してね

上記をdd()した結果がこちらです。

コードについて、priceが等価である場合、第2ソートであるIDを評価する感じです。 どちらを-1にするのか迷ってしまいますが、ロジックは簡単ですね。


2017年6月22日追記
PHP7から導入された宇宙船演算子を利用すればもっと直感的にコーディングすることができます。

$sortedEntities = $entities->sort(function($first, $second) {
     return $first->price <=> $second->price
         ?: $second->id   <=> $first->id;
  });

スキーマビルダーでのソート

コレクションスキーマビルダーは似ているものもありますが、もちろん別のもです。

Modelでのあらかじめソートしまう方がベターな場合はorderBy()を使ってソートしましょう。

単一のソートはこう。

$sortedById = Users::orderBy('id', 'desc')->get();

複合キーでのソートはこうです。

$sortedById = Products::orderBy('price', 'acs')
  ->orderBy('id', 'desc')
  ->get();

スキーマビルダーの方が直感的ですね。
EXPLAINしてファイルソートが出ていないかはチェックしてください。

DBに問い合わせを実行するまでがスキーマビルダー、DB問い合わせ後はコレクションですが、時々混乱してしまいますね。

おわりに

面倒でわかりにくいphp標準関数のarray_multisort()はもう使わなくていいんです。

参考にしたstackoverflowでは、汎用的な関数が実装されておりますので、ぜひご覧になってください。
下記にリンクを掲載します。

この記事はtomita@atuwebがお届けしました。

参考

Laravel 日本語ドキュメント
Laravel 5.3 コレクション
https://readouble.com/laravel/5.3/ja/collections.html

stackoverflow
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


Laravel: Up and Running: a Framework for Building Modern Php Apps 2016/11/28発売予定 (英語)


2017年06月22日:宇宙船演算子について追記
2017年06月22日:Qiitaさんに同様の記事を投稿しました。 => [Laravel]CollectionのMultiple Sort
2016年12月05日:スキーマビルダーでのソートについて追記