【WordPress】複数のタクソノミー・タームで記事を絞り込む方法|tax_queryのANDとORの使い分け

【WordPress】複数カテゴリに属する記事をタクソノミー一覧ページで表示する方法 WordPress

WordPressで「複数のカテゴリー(ターム)に関係する記事をまとめて表示したい」というとき、WP_Querytax_query を使います。ポイントは「いずれかに属する(OR)」なのか「すべてに属する(AND)」なのかをはっきりさせることと、タクソノミーとタームを混同しないことです。

まず用語を整理

  • タクソノミー=分類の種類(例:categorypost_tag、独自の genre
  • ターム=その中の項目(例:category の「ニュース」「スポーツ」)
  • tax_query では taxonomy にタクソノミー、terms にターム(スラッグやID)を渡します
スポンサーリンク

いずれかに属する記事を取得する(OR)

「ニュースまたはスポーツ」のようにどれかに当てはまれば表示したい場合は、1つの条件配列に複数のタームをまとめて渡すだけです。(同じタクソノミー内でのORは、これがいちばんシンプルで確実です。)

WP_Query:ニュース または スポーツ の記事(OR)
$args = array(
  'post_type' => 'post', // 対象の投稿タイプに置き換える
  'tax_query' => array(
    array(
      'taxonomy' => 'category',            // タクソノミー
      'field'    => 'slug',
      'terms'    => array('news', 'sports'), // 複数タームをまとめて指定=OR
    ),
  ),
);

$the_query = new WP_Query($args);

if ($the_query->have_posts()) :
  while ($the_query->have_posts()) : $the_query->the_post();
    the_title('<h3>', '</h3>'); // the_title() は自動で出力する(echoは不要)
  endwhile;
  wp_reset_postdata(); // new WP_Query の後は wp_reset_postdata()
endif;
echo the_title() と wp_reset_query() は誤り
the_title()それ自体が出力する関数なので、echo the_title() は不要です(値を受け取りたいときは echo get_the_title();)。また、new WP_Query() のループ後は wp_reset_postdata() を使います(wp_reset_query()query_posts() 用で、ここでは不適切です)。

すべてに属する記事を取得する(AND)

「ニュースかつおすすめ」のように両方に当てはまる記事だけを出したい場合は、条件配列を分け、relationAND にします。異なるタクソノミーをまたいで絞り込むときもこの形です。

WP_Query:カテゴリ「news」かつ タグ「featured」(AND)
$args = array(
  'post_type' => 'post',
  'tax_query' => array(
    'relation' => 'AND', // すべての条件を満たす
    array(
      'taxonomy' => 'category',
      'field'    => 'slug',
      'terms'    => array('news'),
    ),
    array(
      'taxonomy' => 'post_tag',
      'field'    => 'slug',
      'terms'    => array('featured'),
    ),
  ),
);

$the_query = new WP_Query($args);
OR と AND の使い分け
OR=どれかに属していれば表示(対象を広げる)。同一タクソノミー内なら1つの配列に複数タームでOK。
AND=すべてに属していれば表示(対象を絞る)。条件を配列で分け relation => 'AND'。AND・OR・NOT を組み合わせた複雑な条件はWP_Queryで複雑な条件検索を実装する方法で詳しく解説しています。

アーカイブページ自体に反映するには pre_get_posts

上のコードは別に作るサブループです。「タクソノミー一覧ページ(アーカイブ)そのものに、関連タームの記事も含めたい」場合は、テンプレートで new WP_Query() を作るのではなく、pre_get_postsメインクエリを変更します。これならページネーションも正しく動きます。

functions.php:カテゴリーアーカイブに関連タームも含める
add_action('pre_get_posts', function ($query) {
  if (is_admin() || !$query->is_main_query()) {
    return;
  }
  // 「news」カテゴリーのアーカイブに「sports」の記事も含める例
  if ($query->is_category('news')) {
    $query->set('category_name', 'news,sports'); // カンマ区切りで OR
  }
});

アーカイブのメインクエリを変更する考え方はプラグインなしでページネーションを実装する方法の「404対策」や、検索結果を高速化するカスタムクエリ最適化術(meta_query・tax_queryの使い分け)も参考になります。

よくある質問(FAQ)

Qカスタムタクソノミーとカテゴリーを組み合わせて絞り込むには?
Atax_queryrelation => 'AND' を指定し、カテゴリーの条件とカスタムタクソノミーの条件を別の配列で並べます。両方を満たす記事だけが取得できます。IDで指定するなら field は省略(既定 term_id)、スラッグなら 'field' => 'slug' にします。
Qタクソノミーのターム一覧を表示するには?
Aget_terms(array('taxonomy' => 'my_taxonomy', 'hide_empty' => false)) で取得します。各タームには nameslugterm_idcount があります。カテゴリー取得全般は記事のカテゴリーを取得する方法(完全ガイド)を参照してください。
Q特定カテゴリーの記事が持つ別タクソノミーのタームだけ表示するには?
A対象記事を WP_Query で取得し、各記事に wp_get_object_terms($post_id, '別タクソノミー') を適用してタームを集め、term_id で重複を除いてから表示します。

まとめ

  • OR(いずれか):1つの条件配列に 'terms' => array('news', 'sports')
  • AND(すべて):条件を配列で分け 'relation' => 'AND'
  • 用語taxonomy は分類の種類、terms は項目(混同しない)
  • アーカイブ自体を変えるなら pre_get_posts(サブループの new WP_Query ではない)
  • 後片付けthe_title() はそのまま/wp_reset_postdata() でリセット

「OR で広げる/AND で絞る」を意識し、タクソノミーとタームを取り違えなければ、複数条件の記事一覧は正確に作れます。