LoginSignup
2
1

More than 5 years have passed since last update.

軽量ORMの Idiorm でwhere_inした順に並べ替えする(任意順でorder_by)

Last updated at Posted at 2017-08-11

なにがしたい?

たとえばランキングや閲覧履歴などを表示しようとDBからレコード一覧を取得するとき、任意のレコードが、任意の順番で出てきて欲しいというケース。どうやって order_by するのか? というのを、SQLではなくてORMのIdiormでやってみます。

結論

order_by_expr で生SQLを書きます

Idiormにそんな便利機能が備わっていないだけでなく、SQLとしても全SQL共通の標準機能で備わっていないので、利用しているDBに応じて生 ORDER BY を書いて上げる必要があります。

MySQLの場合

FIELD関数を使用して ORDER BY FIELD( column, data1, data2 .... ) と書きます。dataは単純にimplodeすれば大丈夫なので、下記のようになります。

$items = MyModel::where_in('id',$dataArray)
  ->order_by_expr( 'field( id, '.implode(',',$dataArray).')')
  ->find_many();

PostgreSQLの場合

CASEを使用して ORDER BY CASE WHEN column=data1 THEN 0 WHEN column=data2 THEN 1 ... ELSE N ENDと書きます。長いのでヘルパー関数を用意してFIELDのように簡潔に書くのがオススメ。

$items = MyModel::where_in('id',$dataArray)
  ->order_by_expr( postgres_order_by_array('id',$dataArray) )
  ->find_many();

function postgres_order_by_array( $column, $array ){
    $expr = 'CASE ';
    foreach( $array as $idx => $val ) $expr .= "WHEN $column='$val' THEN $idx ";
    $expr .= 'ELSE '.($idx+1).' END';
    return $expr;
}

ELSE N という部分は、where_in と併用している場合は ELSE に該当するレコードは出てこないので適当でも大丈夫のはずですが、念のため付ける場合、 0 としている文献もありますが、想定外のレコードは後ろに並んでほしいので最大値を振ったほうが良いと思います。

余談

Laravel の場合は orderByRaw というメソッドがあるので、それで同じようにします。最近、プロジェクトAではLaravel+MySQLを使い、プロジェクトBでは環境の制約でLaravelが使えないので「IdiormをLaravel風に使う」(しかもPostgreSQL)という、個人的には二度手間でしか無い車輪の再発明的なことを積極的にやっています…。これはその奮闘の記録です(笑)。

つづく……。

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1