なにがしたい?
たとえばランキングや閲覧履歴などを表示しようと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)という、個人的には二度手間でしか無い車輪の再発明的なことを積極的にやっています…。これはその奮闘の記録です(笑)。
つづく……。