Blog

WordPress 4.2 から可能になった、カスタムフィールドの複数ソート

Posted by admin at 11:16 日時 2015/04/26

#

WordPress4.2でまたもやWP_Query周りの仕様変更がありましたが、なぜかCodexに反映されないので先回りで情報共有です。4.1以前でも、下記のように meta_query パラメーターに配列を渡すことで、複数のカスタムフィールドのキーで検索を行なうことができました。

下記のコードは、商品データベースの検索をイメージしたサンプルです。priceというキーのカスタムフィールドに入った価格情報とreleaseというキーのカスタムフィールドに入った発売日を組み合わせて、1000円以上、かつ2013年以降に発売された商品のみ取得しています(実際の構築においては、おそらく投稿タイプの指定も必要でしょうが、本稿の趣旨とは関係がないため省いています)。

<?php  $args = array(      'meta_query' => array(          'relation' => 'AND',          array(              'key'     => 'price',              'value'   => '1000',              'type'    => 'numeric',              'compare' => '>'          ),          array(              'key'     => 'release',              'value'   => '2013-01-01',              'type'    => 'date',              'compare' => '>'          )      )  );  $query = new WP_Query( $args );

このコードで出力されるSQLは下記のようになります。wp_postmeta テーブルを2回結合しており、2回目に結合した方は mt1 というエイリアスが付けられているのが分かります。

SELECT SQL_CALC_FOUND_ROWS wp_posts.ID FROM wp_posts INNER JOIN wp_postmeta ON ( wp_posts.ID = wp_postmeta.post_id ) INNER JOIN wp_postmeta AS mt1 ON ( wp_posts.ID = mt1.post_id ) WHERE 1=1 AND ( ( wp_postmeta.meta_key = ‘price’ AND CAST(wp_postmeta.meta_value AS SIGNED) > ‘1000’ ) AND ( mt1.meta_key = ‘release’ AND CAST(mt1.meta_value AS DATE) > ‘2013-01-01’ ) ) AND wp_posts.post_type = ‘post’ AND (wp_posts.post_status = ‘publish’ OR wp_posts.post_status = ‘future’ OR wp_posts.post_status = ‘draft’ OR wp_posts.post_status = ‘pending’ OR wp_posts.post_status = ‘private’) GROUP BY wp_posts.ID ORDER BY wp_posts.post_date DESC LIMIT 0, 10

このような検索を行なう場合、まずブログ記事ということは無いでしょうから、ソートも同時に行ないたいことと思います。その場合は、orderby パラメーターに meta_value と指定するのが4.1までのやりかたでした。

<?php  $args = array(      'meta_query' => array(          'relation' => 'AND',          array(              'key'     => 'price',              'value'   => '1000',              'type'    => 'numeric',              'compare' => '>'          ),          array(              'key'     => 'release',              'value'   => '2013-01-01',              'type'    => 'date',              'compare' => '>'          )      ),      'orderby' => 'meta_value'  );  $query = new WP_Query( $args );

出力されるSQLは下記のようになります。ORDER BY句が変化したことが分かります。

SELECT SQL_CALC_FOUND_ROWS wp_posts.ID FROM wp_posts INNER JOIN wp_postmeta ON ( wp_posts.ID = wp_postmeta.post_id ) INNER JOIN wp_postmeta AS mt1 ON ( wp_posts.ID = mt1.post_id ) WHERE 1=1 AND ( ( wp_postmeta.meta_key = ‘price’ AND CAST(wp_postmeta.meta_value AS SIGNED) > ‘1000’ ) AND ( mt1.meta_key = ‘release’ AND CAST(mt1.meta_value AS DATE) > ‘2013-01-01’ ) ) AND wp_posts.post_type = ‘post’ AND (wp_posts.post_status = ‘publish’ OR wp_posts.post_status = ‘future’ OR wp_posts.post_status = ‘draft’ OR wp_posts.post_status = ‘pending’ OR wp_posts.post_status = ‘private’) GROUP BY wp_posts.ID ORDER BY CAST(wp_postmeta.meta_value AS SIGNED) DESC LIMIT 0, 10

ここまで見てきて、じゃあソートキーを複合指定したい場合はどうするんだ、と思われると思いますが、その手段が4.1まではなかったんですね。4.2からは、meta_query に渡す配列にキーを指定することで、ソートキーを明示することが可能になりました。

<?php  $args = array(      'meta_query' => array(          'relation' => 'AND',          'meta_price' => array(              'key'     => 'price',              'value'   => '1000',              'type'    => 'numeric',              'compare' => '>'          ),          'meta_release' => array(              'key'     => 'release',              'value'   => '2013-01-01',              'type'    => 'date',              'compare' => '>'          )      ),      'orderby' => 'meta_release meta_price date'  );  $query = new WP_Query( $args );

この記述で、リリース日の降順でまずソートし、同じリリース日であった場合は価格でソートし、さらに同じ価格であったら投稿日でソートするという複合指定が可能になります。出力されるSQLは下記のようになります。ORDER BY句に変化があることが分かると思います。また、配列で指定したキーは実際のSQLのエイリアスで使われることは無いと言うことにもご注目ください。てっきりエイリアス指定されるもんだと思っていました。

SELECT SQL_CALC_FOUND_ROWS wp_posts.ID FROM wp_posts INNER JOIN wp_postmeta ON ( wp_posts.ID = wp_postmeta.post_id ) INNER JOIN wp_postmeta AS mt1 ON ( wp_posts.ID = mt1.post_id ) WHERE 1=1 AND ( ( wp_postmeta.meta_key = ‘price’ AND CAST(wp_postmeta.meta_value AS SIGNED) > ‘1000’ ) AND ( mt1.meta_key = ‘release’ AND CAST(mt1.meta_value AS DATE) > ‘2013-01-01’ ) ) AND wp_posts.post_type = ‘post’ AND (wp_posts.post_status = ‘publish’ OR wp_posts.post_status = ‘future’ OR wp_posts.post_status = ‘draft’ OR wp_posts.post_status = ‘pending’ OR wp_posts.post_status = ‘private’) GROUP BY wp_posts.ID ORDER BY CAST(mt1.meta_value AS DATE) DESC, CAST(wp_postmeta.meta_value AS SIGNED) DESC, wp_posts.post_date DESC LIMIT 0, 10

orderby パラメーターを配列で指定すれば、昇順と降順を組み合わせることもできます。

<?php  $args = array(      'meta_query' => array(          'relation' => 'AND',          'meta_price' => array(              'key'     => 'price',              'value'   => '1000',              'type'    => 'numeric',              'compare' => '>'          ),          'meta_release' => array(              'key'     => 'release',              'value'   => '2013-01-01',              'type'    => 'date',              'compare' => '>'          )      ),      'orderby' => array(          'meta_price'   => 'asc',          'meta_release' => 'desc',          'date'         => 'desc'      )  );  $query = new WP_Query( $args );

この記述で生成されるSQLは下記のようになります。

SELECT SQL_CALC_FOUND_ROWS wp_posts.ID FROM wp_posts INNER JOIN wp_postmeta ON ( wp_posts.ID = wp_postmeta.post_id ) INNER JOIN wp_postmeta AS mt1 ON ( wp_posts.ID = mt1.post_id ) WHERE 1=1 AND ( ( wp_postmeta.meta_key = ‘price’ AND CAST(wp_postmeta.meta_value AS SIGNED) > ‘1000’ ) AND ( mt1.meta_key = ‘release’ AND CAST(mt1.meta_value AS DATE) > ‘2013-01-01’ ) ) AND wp_posts.post_type = ‘post’ AND (wp_posts.post_status = ‘publish’ OR wp_posts.post_status = ‘future’ OR wp_posts.post_status = ‘draft’ OR wp_posts.post_status = ‘pending’ OR wp_posts.post_status = ‘private’) GROUP BY wp_posts.ID ORDER BY CAST(wp_postmeta.meta_value AS SIGNED) ASC, CAST(mt1.meta_value AS DATE) DESC, wp_posts.post_date DESC LIMIT 0, 10

WordPressのフォーラムでも、何度かソートの複合指定をしたい場合は、フィルターフックでSQLを直接いじくるしか方法がないという回答をしてきましたが、これでようやくフィルターフックの出番はほとんど無くなりそうです。


Share this entry