WordPressのpre_get_posts actionを使ってカスタムフィールドでソートする。

WordPressでカテゴリを表示した場合に独自に設定しているカスタムフィールドでソートをしたい。
WordPressのPost Loopを作成する際の挙動を変更するにはpre_get_posts Actionを利用するのがよさそうだ。(WordPress 4.3を利用中。) 以下の例では、「itemsortno」に登録した値の降順でソートをしている。

/**
 * @param $wp_query WP_Query
 */
function myaction_category_sortorder( $wp_query )
{
    if( is_admin() ){
        return $wp_query;
    }

    if( $wp_query->is_main_query() && $wp_query->is_category() ){
        $wp_query->set( 'meta_key', 'itemsortno' );
        $wp_query->set( 'orderby', 'meta_value' );
        $wp_query->order( 'order', 'DESC' );
    }
    return $wp_query;
}

add_action( 'pre_get_posts', 'myaction_category_sortorder' );

ただし、

独自のカスタムフィールド「itemsortno」は 1つのカテゴリ内の投稿にのみ設定していて、通常の投稿には無い。 カスタムフィールドが未設定のカテゴリを表示すると投稿は表示されなかった。 $wp_queryにmeta_keyを指定するとカスタムフィールドが無い投稿はヒットしないようだ。 WordPressが発行するSQLを見ると、wp_postmeta.meta_key = ‘itemsortno’ のWhere句によって未設定投稿が除外されるようだ。

WordPress 発行SQL
SELECT  wp_posts.*
FROM wp_posts
INNER JOIN wp_term_relationships ON (wp_posts.ID = wp_term_relationships.object_id)
INNER JOIN wp_postmeta ON (wp_posts.ID = wp_postmeta.post_id)
WHERE 1=1 AND (
 wp_term_relationships.term_taxonomy_id IN (1,12)
) AND (
 wp_postmeta.meta_key = 'itemsortno'
) AND wp_posts.post_type = 'post' AND (wp_posts.post_status = 'publish' OR wp_posts.post_status = 'private')
GROUP BY wp_posts.ID
ORDER BY wp_postmeta.meta_value DESC
LIMIT 0, 12

そのため、商品のカテゴリの場合の表示に限定する必要がある。今回はカテゴリを限定して特定カテゴリの場合のみソートオーダーを変更した。


/**
 * @param $wp_query WP_Query
 */
function myaction_category_sortorder( $wp_query )
{
    if( is_admin() ){
        return $wp_query;
    }

    if( $wp_query->is_main_query() && $wp_query->is_category() ){
        $catid =$wp_query->get_queried_object_id(); // カテゴリIDを取得
        if( $catid==5){
            // カテゴリID=5の場合 カスタムフィールド itemsortno 降順
            $wp_query->set( 'meta_key', 'itemsortno' );
            $wp_query->set( 'orderby', 'meta_value' );
            $wp_query->order( 'order', 'DESC' );
        }
        else{
            // その他のカテゴリでは投稿日降順
            $wp_query->set( 'orderby', 'date' );
            $wp_query->order( 'order', 'DESC' );
        }
    }

    return $wp_query;
}

add_action( 'pre_get_posts', 'myaction_category_sortorder' );
pre_get_posts カテゴリスラッグで絞る場合
/**
 * @param $wp_query WP_Query
 */
function myaction_category_sortorder( $wp_query )
{
    if( is_admin() ){
        return $wp_query;
    }

    if( $wp_query->is_main_query() && $wp_query->is_category() ){
        $cat_slug = $wp_query->query_vars['category_name']; // // カテゴリスラッグ取得
        if( $cat_slug=='kaban'){
            // カテゴリスラッグが'kaban'の場合 カスタムフィールド itemsortno 降順
            $wp_query->set( 'meta_key', 'itemsortno' );
            $wp_query->set( 'orderby', 'meta_value' );
            $wp_query->order( 'order', 'DESC' );
        }
        else{
            // その他のカテゴリでは投稿日降順
            $wp_query->set( 'orderby', 'date' );
            $wp_query->order( 'order', 'DESC' );
        }
    }

    return $wp_query;
}

add_action( 'pre_get_posts', 'myaction_category_sortorder' );

これで動作した。