tips

投稿が所属するカテゴリの先祖や子孫を取得するあれこれ。

wordpressの先祖&子孫タクソノミを取得する関数

wordpressを使い始めたころはカスタム投稿というものがなく、ブログもニュースも商品もカテゴリだけで分岐させていたから、カテゴリツリーは複雑な構成になりがちでした。カテゴリスラッグでCSSを充てたくて、子孫カテゴリや先祖カテゴリを調べる関数を見よう見まねで作りました。カスタムタクソノミに対応するように書き直したりしたものの、そもそも階層の深いカテゴリを使わなくなってしまい、お蔵入りに。カテゴリを複雑にするよりも、カスタム投稿タイプのが分かりよいからです。

で、welcart関連のカスタマイズを調べていたら【プラグインなしで直近のカテゴリーだけから関連商品(記事)を表示させる方法】という記事で、最下層カテゴリを取得するために子孫カテゴリーを持たないカテゴリーだけを抽出するくだりに行き当たりました。ネットショップだとカテゴリが複雑になりがちだから、まだ使えるかもしれない。(そんな大きな規模のネットショップとか作りたくないですけどね。)というわけで、何かの役に立つこともあるかと思いなおし、備忘録も兼ねてまとめておくことにしました。

(実用的な応用として、bodyクラスに投稿/カテゴリの所属スラッグをすべて付与する方法も書きましたのでどうぞ。)

 


 

子孫カテゴリを取得する

記事が所属するカテゴリのうち最下層のものだけを取得する

よくあるケースだと、同一カテゴリツリーの階層を複数選択している場合があります。丁寧なユーザーは中間カテゴリや最上位カテゴリにもチェックを入れてしまうことがあるからです。この場合、記事内でget_categoryしてもどのカテゴリが取得されるか分かりません。なので、記事が所属するカテゴリのうち最下層のものだけを取得するようにしてやります。

ちなみに複数ツリーに分岐してセレクトされているときは…

複数ツリーに分岐してカテゴリがセレクトされている場合は、最下層のカテゴリを配列で取得します。

cat_sample2_2

下記関数群をfunctions.phpに記載します

function get_term_descendants ( $post_id ='', $tax_name = 'category' ) {
  $terms = get_the_terms( $post_id, $tax_name );
  if ( empty( $terms )) return false; 
  $candidate = $terms;
  $count = count( $terms );
  if ( $count > 1 ): foreach( $terms as $key => $term ):
    foreach( $terms as $term2 ):
      if ( term_is_ancestor_of( $term->term_id, $term2->term_id, $tax_name ) ) { // 比較対象IDが親系の場合
        unset( $candidate[$key] ); // 比較対象IDを配列から削除
        break;
     }
    endforeach;
  endforeach; endif;
  return $candidate;
}

 

出力の仕方

get_term_descendants( $post->ID, ‘カスタムタクソノミ名’ )とすると、該当カテゴリの配列が返ってきます。カスタムタクソノミ名をなしにすると、通常のカテゴリが出力されます。

$descends = get_term_descendants(); 
if ( $descends ): foreach ( $descends as $descend ):
echo '<!-- タームID:'. $descend->term_id .' ターム名:'. esc_html( $descend->name ) .' タームスラッグ:'. $descend->slug .'-->'. "n";
endforeach; endif;

 

先祖カテゴリを取得する

最上層のカテゴリIDやスラッグを取得する場合

今度は逆に、カテゴリページを表示しているときに、その一番上の階層のカテゴリIDやスラッグを取得したいときがあります。カテゴリ毎にCSSでデザインを変更するときなどですね。(2015/02/04 … get_ancestorsという大変便利な関数の存在をスルーしていたので、シンプルに書き直しました。)

cat_sample3

関数は二組セットでget_term_ancestor_by_term()はカテゴリから祖先カテゴリを求める関数、get_term_ancestor()は投稿ループの中から所属カテゴリを取得するところからスタートし、関数内でget_term_ancestor_by_term()を呼び出しています。

下記関数群をfunctions.phpに記載します

function get_term_ancestor_by_term ( $sbjcat = '', $tax_name = 'category' ) { 
// [$sbjcat = カテゴリIDまたはスラッグ],
// [$tax_name = タクソノミ名]
    if ( empty( $sbjcat ) && is_category() ) {
        $sbjcat = get_query_var( 'cat' ); //カテゴリページ
    } elseif ( empty( $sbjcat ) && is_tax() ) {
        $sbjcat = get_query_var( 'term_id' );
        $tax_name = get_query_var('taxonomy'); //カスタムタクソノミページ
    } elseif ( !is_numeric( $sbjcat ) ) {
        $sbjcat = get_term_by( 'slug', $sbjcat, $tax_name )->term_id; //スラッグからIDを取得
    }
    if ( !$sbjcat ) return false; //カテゴリ取得不可

    $ancestors = array_reverse( get_ancestors( $sbjcat , $tax_name ) ); 
    if( isset( $ancestors[0] ) ) $ancestor_id = $ancestors[0]; else $ancestor_id = $sbjcat;
    return get_term( $ancestor_id , $tax_name );
}

function get_term_ancestor ( $post_id = '', $extree = '', $tax_name = 'category' ) { 
// [$post_id = 投稿ID],
// [$extree = 含めないカテゴリツリーのIDまたはスラッグ],
// [$tax_name = タクソノミ名]
    if ( empty ( $post_id ) ) $post_id = get_post()->ID;
    if ( $extree && !is_numeric( $extree ) ) $extree = get_term_by( 'slug', $extree, $tax_name )->term_id; 
    $terms = get_the_terms( $post_id, $tax_name );
    if ($terms) : foreach ( $terms as $current_term ) : 
        if ( $current_term->term_id !== $extree && !isset( $tia ) ) { // $extreeが親ではない場合
            $req_id = $current_term->term_id;
            break;
        }
    endforeach; endif;
    if ( $req_id ) return get_term_ancestor_by_term( $req_id, $tax_name );
    else return false;
}

 

 

カテゴリから先祖カテゴリを取得する場合

祖先は一つしか返さない仕様になっている(複数の祖先を求めるケースは考えにくいため)のでループする必要はありません。第二引数にカスタムタクソノミ名(省略時はカテゴリ)を指定できます。

$ancestor = get_term_ancestor_by_term ( get_query_var('cat') );
echo '<!-- タームID:'. $ancestor->term_id .' ターム名:'. esc_html( $ancestor->name ) .' タームスラッグ:'. $ancestor->slug .'-->';

 

投稿から先祖カテゴリを取得する場合

ほぼ同様です。ループの必要はありません。

$ancestor = get_term_ancestor( $post->ID );
echo '<!-- タームID:'. $ancestor->term_id .' ターム名:'. esc_html( $ancestor->name ) .' タームスラッグ:'. $ancestor->slug .'-->';

 

複数の先祖に属するカテゴリが設定されている投稿から特定の先祖カテゴリを取得する場合

投稿から先祖カテゴリを取得する場合、複数の先祖に跨がることがあります。

除外したいカテゴリツリーがあることも。たとえば、セール商品や更新情報などのサブカテゴリなどのケースが考えられます。
cat_sample3_2
この場合、「食べ物」か「飲み物」のどちらが取得されるかは、投稿IDの若さに左右されます。それでもいいのですが、特定のカテゴリを除外したい場合もあります。

出力の仕方

たとえば[nomimono]というカテゴリスラッグをもつカテゴリツリーを除外し、かつ先祖カテゴリを取得したい場合は以下の通り。第二引数に除外カテゴリ、第三引数にカスタムタクソノミ名(省略時はカテゴリ)を指定します。

$ancestor = get_term_ancestor( $post->ID, 'nomimono' );
echo '<!-- タームID:'. $ancestor->term_id .' ターム名:'. esc_html( $ancestor->name ) .' タームスラッグ:'. $ancestor->slug .'-->';

ちなみにこの関数でexcludeできるカテゴリID/slugはひとつだけです。複数のツリーを除外したければ適宜改変してください。

 


おまけ:第二階層の先祖カテゴリを取得する

ウェルカートの場合、第二階層のカテゴリが取得したい

サイトの構成によっては、第一階層のカテゴリよりも第二層の方が取得したいということもあります。たとえばウェルカートプラグインの場合、先祖カテゴリが自動的にitemになってしまいますので、分岐させたい場合、第二階層が欲しいです。
cat_sample4

下記関数群をfunctions.phpに記載します

この場合、[item]が取得されてはまずいので、[飲み物]カテゴリが取得されるようにする必要があります。で、やはり除外したいカテゴリツリーがある場合が想定されますすので、その処理も含めるようにします。

function get_welcart_ancestor_by_cat ( $sbjcat = '' ) { 
// [$sbjcat = カテゴリIDまたはスラッグ]
    if ( empty( $sbjcat ) ) 
        $sbjcat = get_query_var('cat'); //カテゴリページ
    elseif ( !is_numeric( $sbjcat ) ) 
        $sbjcat = get_term_by( 'slug', $sbjcat, $tax_name )->term_id;
    if ( !$sbjcat ) return false; //カテゴリ取得不可
    $ancestors = array_reverse( get_ancestors( $sbjcat , 'category' ) ); 
    if ( !$ancestors || get_term_by( 'term_id', $ancestors[0], 'category' )->slug !== 'item' ) return false; //非welcart
    if( isset( $ancestors[1] ) ) $ancestor_id = $ancestors[1]; else $ancestor_id = $sbjcat;
    return get_term( $ancestor_id , 'category' );
}

function get_welcart_ancestor ( $post_id = '', $extree = '' ) { 
// [$post_id = 投稿ID],
// [$extree = 除外するカテゴリツリーのIDまたはスラッグ]
    if ( !$post_id ) $post_id = get_post()->ID;
    $item_ancestor = get_term_by( 'slug', 'item', 'category' );
    $item_ancestor_id = $item_ancestor->term_id; //「アイテム」カテゴリのID
    if ( $extree && !is_numeric( $extree ) ) { 
        $get_extree = get_term_by( 'slug', $extree, 'category' );
        if ( $get_extree ) $extree = $get_extree->term_id; 
    }
    $cats = get_the_terms( $post_id, 'category' );
    if ($cats): foreach ( $cats as $current_cat ) :
        if ( $current_cat->term_id !== $extree && !term_is_ancestor_of ( $extree, $current_cat->term_id, 'category' ) && $current_cat->term_id !== $item_ancestor_id ) {
        // $extreeではないカテゴリで、$extreeが親ではないカテゴリ、かつitemではないカテゴリ、かつuncategorizedではないカテゴリ
            $sbjcat = $current_cat->term_id;
            break;
        }
    endforeach; endif;
    if ( empty( $sbjcat ) ) return $item_ancestor; // 該当カテゴリがない場合、「アイテム」カテゴリを返す
    return get_welcart_ancestor_by_cat( $sbjcat );
}

 

商品シングルテンプレートから呼び出す場合

特定の[sale]というスラッグを持つ商品カテゴリとその子孫を除いて、先祖カテゴリを取得したい場合の出力方法は以下のようになります。

$ancestor = get_welcart_ancestor( $post->ID, 'sale' ); 
echo '<!-- タームID:'. $ancestor->term_id .' ターム名:'. esc_html( $ancestor->name ) .' タームスラッグ:'. $ancestor->slug .'-->';

 

商品カテゴリテンプレートから呼び出す場合

$ancestor = get_welcart_ancestor_by_cat( get_query_var('cat') ); 
echo '<!-- タームID:'. $ancestor->term_id .' ターム名:'. esc_html( $ancestor->name ) .' タームスラッグ:'. $ancestor->slug .'-->';

 

Your Comment

コードの記述は<pre>または<code>タグで括って下さい。自動的にエスケープされます。

 

右の文字を入力して下さい captcha