Eloquent・データベース Eloquent クエリビルダ ソート

モデルをDBから取得するときや、oneToManyのリレーションを並び替えるときに、複数の条件で並び替えたい場合があります。1つ目の条件では値が同じで並び順が決定できず、2つ目の条件で優先順位を決定したい、というケースです。

結論から言うと、方法は2つです。`orderBy()` を必要な数だけ連続でつなぐか、`orderByRaw()` でORDER BY句を直接書きます。どちらもEloquentモデル・クエリビルダー・リレーションのいずれでも同じように使えます。以下でそれぞれの書き方と、内部でどう動いているか、ハマりやすいポイントまで解説します。

複数条件での並び替え方法1 orderByを連続

単純にorderByを複数つなげることで、複数の並び替え条件となります。


Profile::orderBy('ranked_at', 'desc')
->orderBy('rank', 'asc')
->get();

上記の例だと `ORDER BY `ranked_at` DESC, `rank` ASC` というクエリになります。書いた順番がそのままORDER BYの優先順位になるため、まず並び替えたい条件を先に書くのがポイントです。

ここで使っている `orderBy()` は、実はEloquentの `IlluminateDatabaseEloquentBuilder` 自身が持つメソッドではありません。`Builder` クラスのdocコメントには `@mixin IlluminateDatabaseQueryBuilder` と書かれており、`ForwardsCalls` トレイトを使ってクエリビルダー(`IlluminateDatabaseQueryBuilder`)へ処理を転送しています。つまり `Model::orderBy(…)` も内部的にはクエリビルダーの `orderBy` を呼んでいるだけなので、EloquentでもDBファサードのクエリビルダーでも挙動は同じです。


// リレーションでも同じ書き方が使える
$user->posts()
->orderBy('published_at', 'desc')
->orderBy('id', 'desc')
->get();

複数条件での並び替え方法2 orderByRaw

orderByRawを使えば自由にORDER BY句を記述することができます。もしFIELD関数などを使用したい場合はこちらで書いても良いでしょう。

Profile::orderByRaw('ranked_at desc, rank asc')
->get();

`orderByRaw` はカラム名やDESC/ASCをまとめて1つの文字列で渡せるため、`orderBy` を連続で書くより短くまとまります。特定の値の順に並べたい場合は、MySQLの `FIELD()` 関数と組み合わせると便利です。


// 指定したstatusの並び順で取得する
Profile::orderByRaw('FIELD(status, ?, ?, ?)', ['active', 'pending', 'banned'])
->get();

このように第2引数へバインド値を渡せば、変数を安全に埋め込めます。ユーザー入力を文字列連結でORDER BY句に埋め込むとSQLインジェクションの原因になるため、可変値は必ずバインドで渡してください。

複数条件の並び替えでハマりやすい落とし穴

複数条件のorderByは書き方自体は簡単ですが、意図と違う結果になりやすいポイントがいくつかあります。

  • 条件の順番=優先順位:orderByを書いた順にORDER BYへ追加されるため、順番を入れ替えると結果が変わります。「同点のときの二次ソート」を後ろに書くのが基本です。
  • NULLの並び順:DESCで並べるとNULLが先頭・末尾のどちらに来るかはDBによって異なります。狙った位置に固定したい場合は `orderByRaw(‘ranked_at IS NULL, ranked_at desc’)` のように制御します。
  • orderByRawは検証されない:`orderByRaw` に渡した文字列はそのままSQLに展開されます。カラム名を動的に組み立てる場合はホワイトリストで検証し、可変値はバインドを使いましょう。
  • 大量データのソート:`model:prune` のようなバッチ処理でチャンク取得しながら並び替える場合は、ソートに使うカラムへインデックスを張っておかないと遅くなります。

複数条件の並び替えは、まず「同じ値だったときにどのカラムで決着をつけたいか」を整理してから、その優先順位どおりに `orderBy` を並べるか `orderByRaw` にまとめると、意図した順序を確実に再現できます。

カスタマイズに関するお問い合わせはこちら