pre_get_posts()で、is_tax()、get_queried_object()がおかしくなる時

最終更新:2017-04-18 by Joe

メインループにおける、代表的なクエリー変数を取り出す時に使う、is_tax()、get_queried_object()。テーマをカスタマイズしていると、よく使うのですが、同様にpre_get_postsによる、クエリ変数の書き換えと相性が悪いタイミングがあるようです。

 pre_get_postフィルターについて

こちらもテーマ開発者には御用達の関数、メインクエリへのクエリ情報をパースする直前に差し込まれるフィルターです。引数としてメインクエリオブジェクトが渡されてくるので、あとはやりたい放題です。

参考まで、テンプレートの応じて、クエリ投稿数を変更する例

// アクションフィルターを差し込む
add_action( 'pre_get_posts', 'wwwc_customize_queyr' );


function wwwc_customize_queyr( $query ) {
  if ( $query->is_main_query() ) {
    if ( $query->is_front_page() ) {
      $query->set( 'posts_per_page', 20 );
    } else if ( $query->is_tax('developer') ) {
      $query->set( 'posts_per_page', 50 );
    }
}

is_tax()について

こちらは頻出関数です。カスタムタクソノミーを利用していれば必須の関数でしょう。

is_tax(‘category’)は、is_category()と同じ働きです。

get_queried_object()について

get_queried_objectは、メインクエリのクエリ変数で、「代表的なもの」を取り出します。どのテンプレートかで返り値が変わります。まあ、コーデックスと、コード見て、という幹事ではありますが・・

Codex: get_queried_objectより:

if you’re on a single post, it will return the post object
if you’re on a page, it will return the page object
if you’re on an archive page, it will return the post type object
if you’re on a category archive, it will return the category object
if you’re on an author archive, it will return the author object
etc.

single, pageにおいては、投稿オブジェクトが帰ってくるので、自明すぎて、ほとんど使う機会はありません。

よく使うのはアーカイブです。とりわけ、categoryアーカイブ、tagアーカイブ、カスタムtaxonomyアーカイブでは、そのアーカイブページのterm情報が欲しくなるのですが、どのようなtermベースのアーカイブであれば、termオブジェクトを返してくれます。

get_queried_objectがだめになるケース

例えば、タクソノミーアーカイブを、複数のタームでフィルターすると・・・、クエリーは動作するのですが、get_queired_objectは複数のタームが指定される事は考慮されていないので、おかしな値を返してきます。

// アクションフィルターを差し込む
add_action( 'pre_get_posts', 'wwwc_customize_queyr' );

function wwwc_customize_queyr( $query ) {

  $tax = array(
    'taxonomy' => 'language',
    'field'    => 'slug',
    'terms'    => 'python',
    'operator' => 'IN'
  );

  if ( $query->is_main_query() ) {

    // developer タクソノミーアーカイブのとき
    if ( $query->is_tax('developer') ) {

      // languageタクソノミーでさらにフィルターする!
      $query->set( 'tax_query', array( $tax ) );
    }

}

で、

// タクソノミーアーカイブ, 
// taxonomy-developer.phpで実行

// false が変える
var_dump( is_tax('developer') );

// さらに・・・
$term = get_queried_object();
echo $term->name // 'language'

wp-includes/class-wp-query.phpコードをみるとわかるですが、get_queried_object が、reset($tax_query)により、指定されたtax_query配列の先頭を返してくる仕様なようです。

 

ぐぐると、9年前から、チケットが残っていますね。些細なので、あまり優先度が上がらないようです。

#5358 Queried object on multiple tag query holds only first tag

 

これを考慮して、コード修正

// アクションフィルターを差し込む
add_action( 'pre_get_posts', 'wwwc_customize_queyr' );

function wwwc_customize_queyr( $query ) {

 $tax = array(
   'taxonomy' => 'language',
   'field' => 'slug',
   'terms' => 'python',
   'operator' => 'IN'
 );

 if ( $query->is_main_query() ) {

 // developer タクソノミーアーカイブのとき
 if ( $query->is_tax('developer') ) {
 
    // もともとのtaxクエリ変数、developerを先頭に入れておく
    $tax_orig = $query->tax_query->queries[0];
    
    // languageタクソノミーでさらにフィルターする!
    $query->set( 'tax_query', array( $tax_orig, $tax ) );
 }

}

上記コードはテストしていませんが、get_queried_objectでdeveloperタームが帰ってくれば成功です。

pre_get_postフィルターまとめ

やはりメインクエリを書き換えているので、もしかすると上記以外にもおかしな挙動が発生する場合はあるかもしれませんね。でもちゃんとコードを追っていけば、なんとか回避できそうな気もします。まあ、手間ですけどね。