WordPressのカスタム投稿をタクソノミーのタームで条件分岐する方法

WordPress(ワードプレス)

WordPressでカスタム投稿タイプを使っていると、「このタームが付いた投稿のときだけ、シングルページの見た目やコンテンツを変えたい」という場面が必ず出てきます。私も自作テーマで商品カテゴリーごとにレイアウトを出し分けようとして、どの関数で判定すればいいのか分からず手が止まった経験があります。

タクソノミーのタームで分岐したいだけなのに、is_tax()を単体ページで書いても効かない、といったつまずきはよくある話です。ここではシングルページとアーカイブページ、それぞれで確実に動く条件分岐の書き方を、現行のWordPressの仕様に合わせて整理します。

シングルページはhas_term()で判定する

カスタム投稿タイプのシングルページで「この投稿が特定のタームを持っているか」を調べたいときは、has_term()を使います。WordPress公式のリファレンスでも、テンプレート内で投稿のタームを判定する条件分岐タグとして案内されている関数です。

関数の形は次のとおりです。

has_term( string|int|array $term = '', string $taxonomy = '', int|WP_Post|null $post = null ): bool

第1引数にターム(名前・ID・スラッグのいずれか、または配列)、第2引数にタクソノミー名を渡します。第3引数の投稿は省略するとループ内の現在の投稿が対象になるので、シングルページのメインループ内なら特に指定しなくても動きます。戻り値は真偽値です。

以前このサイトではis_object_in_term()を使う例を載せていましたが、こちらは内部処理向けの低レベル関数です。テンプレート側の条件分岐なら、投稿 IDの取り回しまで面倒を見てくれるhas_term()のほうが素直に書けます。

特定のタームを持つ投稿だけ処理を変える

例えばタクソノミー「product_cat」に「new」というタームが付いた投稿だけ、別の表示にしたい場合はこう書きます。

<?php if ( has_term( 'new', 'product_cat' ) ) : ?>
    <!-- ターム new を持つ投稿のときの処理 -->
<?php else : ?>
    <!-- それ以外の投稿のときの処理 -->
<?php endif; ?>

複数のタームのどれかに当てはまれば処理したい、というときは第1引数を配列で渡します。「new」か「sale」のどちらかを持っていればtrueになります。

<?php if ( has_term( array( 'new', 'sale' ), 'product_cat' ) ) : ?>
    <!-- new または sale を持つ投稿のときの処理 -->
<?php endif; ?>

ループの外で特定の投稿を判定したいときは、第3引数に投稿 IDやWP_Postを渡します。

<?php if ( has_term( 'new', 'product_cat', $post->ID ) ) : ?>
    <!-- $post が new を持つときの処理 -->
<?php endif; ?>

どのタームか細かく分けたいときはget_the_terms()

「持っているかどうか」ではなく、「付いているタームによって処理を何通りにも分けたい」ときは、get_the_terms()で投稿のターム一覧を取り出して自分で判定するほうが扱いやすくなります。

get_the_terms( int|WP_Post $post, string $taxonomy ): WP_Term[]|false|WP_Error

この関数は成功するとWP_Termオブジェクトの配列を返しますが、タームが無ければfalse、タクソノミーが存在しないなどの失敗時にはWP_Errorを返します。そのため公式リファレンスでも、falseとWP_Errorの両方をチェックしてから使うことが推奨されています。スラッグの配列を作ってin_array()で判定すると分岐が書きやすいです。

<?php
$terms = get_the_terms( $post->ID, 'product_cat' );
if ( $terms && ! is_wp_error( $terms ) ) {
    $slugs = wp_list_pluck( $terms, 'slug' );
    if ( in_array( 'new', $slugs, true ) ) {
        // new のときの処理
    } elseif ( in_array( 'sale', $slugs, true ) ) {
        // sale のときの処理
    }
}
?>

wp_list_pluckでスラッグだけの配列にしておくと、in_array()での判定がすっきりします。第3引数のtrueは厳密比較の指定で、意図しない一致を防げます。

アーカイブページはis_tax()で判定する

一方、タクソノミーのアーカイブページ側で分岐したいときはis_tax()を使います。has_term()が「投稿がタームを持つか」を見るのに対し、is_tax()は「いま表示しているページがそのタクソノミーのアーカイブか」を見る、という違いがあります。シングルページで書いても効かないのはこのためです。

is_tax( string|string[] $taxonomy = '', int|string|int[]|string[] $term = '' ): bool

注意点として、is_tax()は組み込みのカテゴリー・タグのアーカイブではfalseを返し、カスタムタクソノミーのアーカイブでtrueになります。またメインクエリの実行後でないと正しく判定できないので、クエリより前で呼ぶとfalseになる点も覚えておくとハマりません。

タクソノミー「product_cat」のアーカイブかどうかで分けるなら、こう書きます。

<?php if ( is_tax( 'product_cat' ) ) : ?>
    <!-- product_cat のアーカイブのときの処理 -->
<?php endif; ?>

特定のタームのアーカイブに絞りたいときは、第2引数にターム(配列も可)を渡します。

<?php if ( is_tax( 'product_cat', array( 'new', 'sale' ) ) ) : ?>
    <!-- new または sale のアーカイブのときの処理 -->
<?php endif; ?>

テンプレートでの使い分けのまとめ

迷ったときは、判定したい対象で選ぶと間違えません。

  • シングルページで投稿のタームを判定 … has_term()。持っているかどうかのシンプルな分岐向き。
  • タームごとに処理を何通りにも分けたい … get_the_terms()で一覧を取り、in_array()で判定。falseとWP_Errorのチェックを忘れずに。
  • アーカイブページの種類で判定 … is_tax()。クエリ実行後に呼ぶこと。

この3つの役割の違いさえ押さえておけば、カスタム投稿タイプのテンプレートで「どこで何を判定するか」に迷わなくなります。まずはhas_term()とis_tax()を、シングルとアーカイブで書き分けるところから試してみてください。