※ order も複数指定できるよ、って話を以前どこかで見た気がするけど仕様変更されたのかしら。
コアのソースを見てみよう
query_posts()
や素の get_posts()
は WP3.2.1 wp-includes/query.php WP_Query::&get_posts()
を呼んでます。で、当該処理は以下のようになってます。
if ( empty($q['order']) || ((strtoupper($q['order']) != 'ASC') && (strtoupper($q['order']) != 'DESC')) )
$q['order'] = 'DESC'; // ■order は ASC か DESC じゃなければ DESC になります
// Order by
if ( empty($q['orderby']) ) {
$orderby = "$wpdb->posts.post_date " . $q['order'];
} elseif ( 'none' == $q['orderby'] ) {
$orderby = '';
} else {
// Used to filter values
$allowed_keys = array('author', 'date', 'title', 'modified', 'menu_order', 'parent', 'ID', 'rand', 'comment_count');
if ( !empty($q['meta_key']) ) {
$allowed_keys[] = $q['meta_key'];
$allowed_keys[] = 'meta_value';
$allowed_keys[] = 'meta_value_num';
}
$q['orderby'] = urldecode($q['orderby']);
$q['orderby'] = addslashes_gpc($q['orderby']);
$orderby_array = array();
foreach ( explode( ' ', $q['orderby'] ) as $i => $orderby ) { // ■orderby は半角スペース区切り!
// Only allow certain values for safety
if ( ! in_array($orderby, $allowed_keys) )
continue;
switch ( $orderby ) {
case 'menu_order':
break;
case 'ID':
$orderby = "$wpdb->posts.ID";
break;
case 'rand':
$orderby = 'RAND()';
break;
case $q['meta_key']:
case 'meta_value':
$orderby = "$wpdb->postmeta.meta_value";
break;
case 'meta_value_num':
$orderby = "$wpdb->postmeta.meta_value+0";
break;
case 'comment_count':
$orderby = "$wpdb->posts.comment_count";
break;
default:
$orderby = "$wpdb->posts.post_" . $orderby;
}
$orderby_array[] = $orderby;
}
$orderby = implode( ',', $orderby_array ); // ■orderby パラメータを半角コンマで連結
if ( empty( $orderby ) )
$orderby = "$wpdb->posts.post_date ".$q['order']; // ■orderby 指定が無効な場合は日付の降順
else
$orderby .= " {$q['order']}"; // ■最後に order パラメータを追加
}
ポイントとなる箇所にコメントを記載してます。これで、
- order は ASC, DESC どちらか1つのみ
- orderby は半角スペース区切りで複数指定可能
- order パラメータは半角コンマ区切りで連結した orderby の最後に追加される
という現状把握ができました。
フィルタで ORDER BY 節をいじろう
節はブシじゃないですよ。WP_Query::&get_posts()
ではSQL 文の各節ごとにフィルタが用意されているので書き換えは簡単です。
if ( !$q['suppress_filters'] ) { // ■get_posts() はこれがデフォルトで true!
$where = apply_filters_ref_array( 'posts_where_paged', array( $where, &$this ) );
$groupby = apply_filters_ref_array( 'posts_groupby', array( $groupby, &$this ) );
$join = apply_filters_ref_array( 'posts_join_paged', array( $join, &$this ) );
$orderby = apply_filters_ref_array( 'posts_orderby', array( $orderby, &$this ) ); // ■これを使う!
$distinct = apply_filters_ref_array( 'posts_distinct', array( $distinct, &$this ) );
$limits = apply_filters_ref_array( 'post_limits', array( $limits, &$this ) );
$fields = apply_filters_ref_array( 'posts_fields', array( $fields, &$this ) );
// Filter all clauses at once, for convenience
$clauses = (array) apply_filters_ref_array( 'posts_clauses', array( compact( $pieces ), &$this ) );
foreach ( $pieces as $piece )
$$piece = isset( $clauses[ $piece ] ) ? $clauses[ $piece ] : '';
}
suppress_filters
というパラメータはその名の通り、true
に設定されているとフィルタを実行しません。WP_Query::&get_posts()
や query_posts()
を使うときは敢えて true
を指定しない限り false
になるので何も心配しなくてもフィルタ実行してくれます。が、get_posts()
という素の関数ではデフォルトパラメータとしてこれが true
という仕様なのでフィルタを使用したい場合は false
を明示的に指定します。以下にまとめますね。
// 気にしなくて OK!
query_posts( array( 'orderby' => 'date', 'order' => 'ASC' ) );
/ /気にしなくて OK!
$posts = new WP_query( array( 'orderby' => 'date', 'order' => 'ASC' );
// 気にしなくて OK!
$my_query = new WP_query();
$posts = $my_query->get_posts( array( 'orderby' => 'date', 'order' => 'ASC' ) );
// 気にして!false にして!
$posts = get_posts( array( 'orderby' => 'date', 'order' => 'ASC', 'suppress_filters' => false ) );
order パラメータの与え方
さて、order
に複数パラメータを与えて posts_orderby
フィルタで ORDER BY 節を変更したいところですが、このフィルタが呼ばれるのは、先ほどのソースの一番上のコメント部分「 ■order は ASC か DESC じゃなければ DESC になります」より後なので、複数パラメータを与えても既に DESC
に変えられてます。
なので、ここでは別のパラメータ myorder
を創作してそれに複数 order
を指定するようにします。以下、パラメータの与え方。
// 例)メタキー my_meta の値で降順 --> 日付で降順にする場合
$args = array(
'meta_key' => 'my_meta',
'orderby' => 'meta_value_num date',
'myorder' => 'DESC DESC', // ■創作パラメータ
// 'suppress_filters' => false, // ■素の get_posts() で使う場合はこれも必要!
);
query_posts( $args );
:
フィルタの処理
以下を functions.php に追加すれば OK です。
add_filter( 'posts_orderby','my_posts_orderby', 10, 2 );
function my_posts_orderby( $orderby, $query ) {
$orders = array_filter( explode( ' ', strtoupper( $query->get( 'myorder' ) ) ) );
if ( 0 < count( $orders ) ) { // ■myorder にパラメータが設定されている場合:
$orderby_array = array();
foreach ( explode( ',', str_replace( ' DESC', '', $orderby ) ) as $i => $the_orderby ) { // ■最後の DESC を取って半角カンマで項目を分割
if ( ! isset( $orders[$i] ) || ! in_array( $orders[$i], array( 'ASC', 'DESC') ) ) // ■ASC, DESC 以外の場合:
$orderby_array[] = $the_orderby . ' DESC' ; // ■DESC にする
else // ■ASC または DESC なら:
$orderby_array[] = $the_orderby . ' ' . $orders[$i]; // ■項目の直後に並び順を追加
}
$orderby = implode( ',', $orderby_array ); // ■並び順が追加された項目達を連結
}
return $orderby; // ■いってらっしゃい!
}
myorder にパラメータが設定されていなくても、myorder にパラメータが設定されている場合にいっちゃう不具合を修正しました。
動作確認バージョン
- WordPress 3.1.3
【ブログ】query_posts, get_posts で複数の order 指定に対応する http://bit.ly/rfxbnb #wordpressjp
有用な記事ありがとうございます。
漢字交じりの投稿のタイトルを五十音順に並べるのに苦慮していましたが
このページのコードを参考にする事で解決できました。
あなた天才です!!
query_posts, get_posts で複数の order 指定に対応する | wpxtreme http://t.co/3T2bm067
orderby