SEEDS Creator's Blog

複数のカスタムフィールドでソート

WordPressでの特殊なソート方法。

通常WordPressの記事取得は以下のような形で行えます。 以下の感じだとカスタム投稿タイプhogehogeの記事を取得してきます。

$args = array(
     'post_type'      => 'hogehoge',
);
query_posts( $args );

ソート

WordPressの記事の取得はorderbyで指定した内容の並び順にする事ができます。 指定できる内容は以下の通り。

'ID' - post id 順
'author' - 投稿者順
'title' - タイトル順
'date' - 投稿日付順
'modified' - 最終更新日順
'parent' - 親記事のID順
'rand' - ランダムにする
'comment_count' - コメント数で並べ替え(バージョン 2.9 以降のみ)
'menu_order' - 記事のメニューオーダー順
'meta_value' - 指定したカスタムフィールド順
'meta_value_num' - カスタムフィールドの値を数値として扱ってソート(1と10とかのソートをちゃんとしてくれる)

記事の並びをタイトル順にしたいときは以下のようにします。

$args = array(
     'post_type'      => 'hogehoge',
     'orderby'        => 'title',
     'order'          => 'DESC'
);
query_posts( $args );

このとき、「タイトルで並び替えた後に日付順で並び替えたい」という時はこんな感じにスペース区切りで複数指定すればOKです。

$args = array(
     'post_type'      => 'hogehoge',
     'orderby'        => 'title date',
     'order'          => 'DESC'
);
query_posts( $args );

さらにカスタムフィールドの値でもソート可能です。meta_keyでカスタムフィールドを指定し、orderbyでmeta_valueを選択します。 以下の例だと、カスタムフィールド「地域」の値でソートを行います。

$args = array(
     'post_type'      => 'hogehoge',
     'meta_key'       => '地域',
     'orderby'        => 'meta_value',
     'order'          => 'DESC'
);
query_posts( $args );

複数のカスタムフィールドでソート

ここで記事タイトルにもある、複数のカスタムフィールドでのソート方法。 特定のWordPressで上記のことを実現する必要が出てきたので調べてみましたが、 query_postsでは複数のカスタムフィールドでソートする方法はないようです。

途方にくれてたところWordPressにはadd filterというDBや出力前に関数をフックする機能があり、
さらにプラグイン API/フィルターフック一覧にSQLクエリのORDER BYやJOINにフックさせるAPIが用意されておりこの機能を使って実現する事が出来ました。

たとえば「国」というカスタムフィールドの値でソートした後に、「町」というカスタムフィールドでソートしたい場合。

function.phpにて以下のような関数を作成します。
これは単純に動作させたいSQL文を作っているだけです。

//function.php
function custom_posts_join( $join, $query ) {
  global $wpdb;
  $join .= " INNER JOIN $wpdb->postmeta AS m1 ON m1.post_id = $wpdb->posts.ID AND m1.meta_key = '国'";
  $join .= " INNER JOIN $wpdb->postmeta AS m2 ON m2.post_id = $wpdb->posts.ID AND m2.meta_key = '町'";
  return $join;
}

function custom_posts_orderby( $orderby, $query ) {
  $orderby = 'm1.meta_value ASC, m2.meta_value ASC';
  return $orderby;
}

実際のテンプレート内では以下のように使用します。
add_filterやremove_filterの第一引数はフィルターフックの指定で、第二引数が上記で作成した関数を指定します。
第三引数はプライオリティで第四引数は関数が受け取る引数の数となります。

//template
add_filter( 'posts_join', 'custom_posts_join', 10, 2 );
add_filter( 'posts_orderby','custom_posts_orderby', 10, 2 );

$args = array(
     'post_type'      => 'hogehoge',
);
query_posts( $args );
if(have_posts()): while(have_posts()): the_post();

~
HTMLをかいて出力させる
~

endwhile; endif;
remove_filter( 'posts_join', 'custom_posts_join', 10, 2 );
remove_filter( 'posts_orderby','custom_posts_orderby', 10, 2 );

結構実現まで時間がかかりました・・・。