かもメモ

自分の落ちた落とし穴に何度も落ちる人のメモ帳

WordPress 投稿の並び順の条件(orderby)を複数にして、それぞれの並べ方(order)を別々に指定したい。


前回WordPressの投稿の並び替え条件(orderby)を複数にする方法を書きました。

しかし、この方法ではorderbyを複数にすることはできましたが、それぞれの条件で並べ方(order)を指定することはできませんでした。
今回はorderbyを複数にして、それぞれの条件ごとにorderを指定できる方法をがんばります。

ORDER BY 節を変更するフィルター

posts_orderbyというフィルターを使えばWordPressが投稿を取ってくるのに発行するSQLクエリーのORDER BY節を変更することができるようです。

pre_get_postsorderbyorderを複数指定してどんなORDER BYができているのか調べてみます。
条件は前回の記事と同じで発売日(release_time)というカスタムフィールドの値で並べて、発売日が同じだったら投稿日(date)で並べます。今回はそれぞれの並べ方を変更したいので発売日(release_time)は古い順(昇順)で、投稿日(date)は新しい順(降順)にしたいと思います。

<?php // function.php
function change_posts_query( $query ) {
  if( is_archive() ) {
    $query->set('meta_key', 'release_time');
    $query->set('orderby', 'meta_value_num date');
    // orderby のように スペース区切りで複数指定してみる
    $query->set('order', 'ASC DESC');
  }
}
add_action('pre_get_posts', 'change_posts_query');

// ORDER BY節を変更するフィルター
add_filter( 'posts_orderby','change_posts_orderby', 10, 2 );
function change_posts_orderby($orderby, $query) {
  // どんな ORDER BY になっているか見たいだけなのでとりあえず出力する
  var_dump($orderby); // 'wp_postmeta.meta_value+0 DESC, wp_posts.post_date DESC'
  
  // 変更しない場合も$orderby を返してあげないと上手く動作しなくなる
  return $orderby;
}

どうやら、$query->set('order', $value)ASCDESCで指定しないとデフォルトのDESCに置き換え得られてしまうようです。そしてorderbyも$query->setした値のままではないので引数の$orderbyを利用して新しいORDER BY節を作るようにすれば良さそうです。  

並び方(order)を条件ごとに指定できるようにする

$query->set('order', $value)では並び方(order)を複数渡すことができなかったので、新しくキーを作って並び方(order)を複数渡し、posts_orderbyフィルターでORDER BYを組み立てたいと思います。

<?php // function.php
function change_posts_query( $query ) {
  if( is_archive() ) {
    $query->set('meta_key', 'release_time');
    $query->set('orderby', 'meta_value_num date');
    $query->set('order', 'DESC');
    // 独自のキーを作って orderを指定する
    // orderby の指定の仕方と合わせて スペース区切りにすることにします
    $query->set('my_orders', 'ASC DESC');
  }
}
add_action('pre_get_posts', 'change_posts_query');

// ORDER BY節を変更するフィルター
add_filter( 'posts_orderby','change_posts_orderby', 10, 2 );
function change_posts_orderby($orderby, $query) {
  // my_orders をスペース区切りで配列に変換する。 ※ my_orders が無ければ値が0個の配列になる
  $orders = array_filter( explode(' ', strtoupper( $query->get('my_orders') )) );
  // my_orders がある時だけORDER BY節を変更する
  if( count($orders) > 0 ) {
    $orderby_arg = array();
    $order_arg = array('DESC', 'ASC');
    // $orderby文のDESC, ASC を削除して , で分割
    foreach( explode(',', str_replace($order_arg, '', $orderby)) as $i => $the_orderby ) {
      if( isset($orders[$i]) && in_array($orders[$i], $order_arg) ) {
        // 対応する order がある時
        $orderby_arg[] = trim($the_orderby) . ' ' . $orders[$i];
      } else {
        // 対応する order が無い あるいは DESC, ASCでない場合は デフォルト値 DESC を使う
        $orderby_arg[] = trim($the_orderby) . ' DESC';
      }
    }
    // それぞれの orderby の入った配列を , で連結した文字列にする
    $orderby = implode(',', $orderby_arg);
  }
  
  return $orderby;
}

これでorderbymy_ordersともにスペース区切りで対応した並べ方の条件(orderby)に対応する並べ方(order)を指定できるようになりました。
対応するorderが無かったりDESCやASCでない文字だった時はデフォルトのDESCになるようになっているので、orderbyを3つ指定してmy_ordersは2つしか指定しなくても最後の条件は自動的にDESCが割当てられるようになっています。  


[参考]


追記 2015 12/25