WordPress の階層があるカテゴリー、タクソノミーで、親や最上位祖先を取得したり、条件分岐で使える関数を作ったりします。
階層があるタクソノミーに関する関数を作る
カテゴリーを含むタクソノミーに階層がある場合の、親タームや最上位タームに関する関数をいくつか作っていきます。
既存のWordPress関数を拡張させたようなタイプの関数で、階層があるタクソノミーで使用します。カテゴリーに限定せず、タクソノミー全部に対応します。
例:カテゴリーに階層があり arts > images > flyer であるとき、
- ポスト
- 階層があるタームの親や最上位を取得する関数
“flyer” がセットされたポストに対して問いかけると、最上位 “arts” や直属の親 “images” の名前やスラッグを取得します。
- 現在のタームが指定したタームの子タームかどうかを判定する関数
“flyer” がセットされたポストで “arts” を指定して arts に含まれるか?と問うと true を返します。 - 投稿に関連する指定タームに親タームがあるかどうかを判定する関数
“flyer” がセットされたポストで “flyer” を指定して親はあるか?と問うと、true を返します。
- 階層があるタームの親や最上位を取得する関数
- アーカイブ
- 指定したターム以下の階層かどうかを判定する関数
アーカイブで “arts” のアーカイブか?と問うと、”images” や “flyer” のアーカイブでも true を返します。
- 指定したターム以下の階層かどうかを判定する関数
階層があるタームの親や最上位を取得する関数
postにセットされているカテゴリーやタームの、親または最上位を取得します。
get_the_category や get_the_terms では現在セットされているタームを取得しますが、こちらはそのタームが子階層の場合、親や最上位を取得します。取得できるのは名前、スラッグ、term_id のいずれか。
作ってみた関数は以下の二つです。
get_the_parent_term と get_the_root_term
// 親を取得する関数
get_the_parent_term( $taxonomy, $result=null, $post_id=0 )
// 最上位 root を取得する関数
get_the_parent_term( $taxonomy, $result=null, $post_id=0 )
引数:
$taxonomy … タクソノミー名を指定。カテゴリーなら category 。
$result … 結果に何を返すのかを指定。”name” または “term_id” または “slug” を指定する。省略すると slug になります。
$post_id 省略すると現在のポストになります。
たとえば arts > images > flyer と階層があるカテゴリーの、現在 flyer がセットされてる場合、
get_the_parent_team('category', 'slug' );
を実行すると「arts」を返します。現在が images なら arts を返します。現在が arts なら何も返しません(false)
タクソノミー1個だけに対応
複数の子タームがセットされている場合は見つかった最初のタクソノミー1個だけを処理します。
複数に対応すると処理がややこしいので当面一個のみ対応ということにしております。階層のあるタクソノミーを無闇にセットしないという己への戒めでもあります。
関数
直属の親タームを取得する関数
// 親を取得 function get_the_parent_term( $taxonomy, $result, $post_id = 0 ) { // get_the_terms() からの返り値がWP_Errorではないか確認 $terms = get_the_terms( $post_id, $taxonomy ); if ( is_wp_error( $terms ) || empty( $terms ) ) { return false; } // 取得したタームの最初の要素(タクソノミー)を取得 $term = array_shift( $terms ); // get_ancestors() の返り値が空でないか確認 $ancestors = get_ancestors( $term->term_id, $taxonomy ); if ( empty( $ancestors ) ) { return false; } // 祖先の最初の要素を取得 $root_id = array_shift( $ancestors ); // get_term() の返り値がWP_Errorではないか確認 $root_term = get_term( $root_id, $taxonomy ); if ( is_wp_error( $root_term ) ) { return false; } // スラッグとidと名前を取得 $slug = $root_term->slug; $name = $root_term->name; $term_id = $root_term->term_id; // $result に応じて名前かIDかスラッグを返す if ( $result == 'name' ) { return $name; } elseif($result == 'term_id') { return $term_id; } else { return $slug; } }
最上位の親(root)タームを取得する関数
// 親を取得 function get_the_parent_term( $taxonomy, $result, $post_id = 0 ) { // get_the_terms() からの返り値がWP_Errorではないか確認 $terms = get_the_terms( $post_id, $taxonomy ); if ( is_wp_error( $terms ) || empty( $terms ) ) { return false; } // 取得したタームの最初の要素(タクソノミー)を取得 $term = array_shift( $terms ); // get_ancestors() の返り値が空でないか確認 $ancestors = get_ancestors( $term->term_id, $taxonomy ); if ( empty( $ancestors ) ) { return false; } // 祖先の最初の要素を取得 $root_id = array_pop( $ancestors ); // get_term() の返り値がWP_Errorではないか確認 $root_term = get_term( $root_id, $taxonomy ); if ( is_wp_error( $root_term ) ) { return false; } // スラッグとidと名前を取得 $slug = $root_term->slug; $name = $root_term->name; $term_id = $root_term->term_id; // $result に応じて名前かIDかスラッグを返す if ( $result == 'name' ) { return $name; } elseif($result == 'term_id') { return $term_id; } else { return $slug; } }
解説
何かをする度にエラー処理を挟んでいるのでややこしいですが、流れはこうです。
- get_the_terms でターム(配列の配列)を取得
- 配列の最初の一個を取得(タクソノミー1個のみを処理するので)
- get_ancestors で、配列から祖先の term_id の配列を作る
- array_shift で祖先term_idの配列から最初の1個を取得(直属の親)
array_pop で祖先term_idの配列から最後の1個を取得(最上位root) - オブジェクトから name、term_id、slugを取得しておき、
- 指定した $result ( name or term_id or slug or ” )に応じて出力
get_ancestors … これはterm_idから祖先のterm_idsを取得するWordPress関数です。この関数の結果は配列で、直属の親 → その親 → その親 の順に並びます。一番最初が直属の親、一番最後が最上位、ルートの親です。
直属の親を取得するなら array_shift で最初をゲット、
最上位のルートを取得するなら array_pop で最後をゲットします。
親と最上位を取得する関数の違いは、ここだけです。この僅かな違いだけで、わざわざ関数を二つ登録しています。
使いどころや反省点
get_the_parent_term( ) と get_the_root_term( ) は、途中の1行が array_shift か array_pop かの違いだけ・・・ちょっと無駄な感じもします。
一つの関数にして、引数に parent か root を指定するだけでもいいんじゃないかと思わずにおれません。
しかしそれ以前に、実際この関数を何処で使っているのかというと・・・なんと今はテンプレート内でもあまり使用していませんでした。以前は使ってたんだけどなあ。
そんなわけで、この関数が有用なのかどうなのか、ちょっと判断できません。
現在のタームが指定したタームの子タームかどうかを判定する関数
条件分岐の関数で、現在のタームが指定したタームの子タームなら ture を、そうでなければ false を返します。
作ってみた関数はこれです。
has_root_term
has_root_term( $taxonomy, $term_slug )
引数
$taxonomy … タクソノミーです。カテゴリーなら “category”
$term_slug … 最上位rootのターム(または子を持つ親ターム)を指定します。
たとえば arts > images > flyer と、階層があるカテゴリーがあり、has_root_term(‘category’, ‘arts’ )を実行した場合、現在 flyer がセットされていれば、true を返します。
関数
function has_root_term($taxonomy, $root_term_slug, $post_id = null) { if (!$post_id) { $post_id = get_the_ID(); } $terms = wp_get_post_terms($post_id, $taxonomy); foreach ($terms as $term) { if (is_term_root($term, $root_term_slug)) { return true; } } return false; } function is_term_root($term, $root_term_slug) { if ($term->parent == 0 && $term->slug == $root_term_slug) { return true; } elseif ($term->parent != 0) { $parent_term = get_term($term->parent); return is_term_root($parent_term, $root_term_slug); } return false; }
解説
- 指定された post_id に対して taxonomy のタームを取得。
- 各タームについて、is_term_root 関数を呼び出してタームがルートタームかどうかをチェック。
- is_term_root 関数は、タームがルートタームかどうか(親タームがないかどうか)を再帰的にチェック。
- 一致する場合は true を返し、一致しない場合は false を返す。
実際に使うのは if の中になります。
if ( has_root_term('category', 'arts' ) ){ // true の処理 } else { // false の処理 }
もし(その投稿に関連づけられたタームが) ‘arts’ に含まれるなら。という条件分岐ですが、’arts’ の子階層 ‘images’ や ‘flyer’ が関連づけられていても true となります。
使いどころ・反省点
テンプレート以外では、widget の表示・非表示をコントロールする時によく使います。シングルページで、arts 関連全般(子ターム含む)に対して条件を発動できます。
has_root_term という関数名ですが、これちょっとニュアンス的にどうかなと。has_term_parent とか has_term_root としたほうがしっくりくるかも。
カテゴリーでよく使う in_category 関数があります。これの拡張版みたいな関数ですから、「in_term」みたいな in_ でも理解しやすいかもしれない。まあ名前なんてどうでも・・・とあまり思えないんですよねえ。
大分類 – 中分類 -小分類 とカテゴリーを分けている場合、大分類だけ指定すれば以下全部含めて判定、っていうのは使い勝手が良いです。
投稿に関連する指定タームに親タームがあるかどうかを判定する関数
投稿で、指定したタームに親があるかどうかを判定します。
has_parent_term
has_parent_term($taxonomy, $term_slug, $post_id = null)
カテゴリーに階層があり( arts > images > flyer )現在投稿に ‘flyer’ が関連づけられているとき、
has_parent_term( 'category', 'flyer' )
と指定すると true が返ります。「親はおるよ」と答えるわけですね。
引数
$taxonomy … タクソノミー
$term_slug … タームのスラッグ 現在関連づけられているターム
関数
function has_parent_term($taxonomy, $term_slug, $post_id = null) { // 投稿 ID が指定されていない場合は、現在の投稿を使用する if (!$post_id) { $post_id = get_the_ID(); } // 投稿に関連付けられた指定されたタクソノミーのすべてのタームを取得 $terms = wp_get_post_terms($post_id, $taxonomy); // 指定されたタームが親タームであるか子孫であるかを確認 foreach ($terms as $term) { // タームが指定されたタームであるか子孫であるかを確認する if ($term->slug == $term_slug || term_is_ancestor_of($term_slug, $term, $taxonomy)) { return true; // 指定されたタームまたは子孫が見つかった } } return false; // 指定されたタームまたは子孫が見つからない }
解説
post に関連付けされたすべてのタームを総ナメして、指定タームが親であるか子孫であるかを調べます。親として見つかった時点で true を返して終了します。
使いどころ・反省点
使うとすればテンプレート内になるでしょう。タームを表示させる際に if で親があるかどうかを調べ、あれば親タームまたはルートタームを表示したりリンクを作ったりします。
ただ、実際のテンプレートで使用することが減りました。以前は使ってたんですが。ターム関連の関数、もっと詳細なコントロールを別途作ったからです。別途詳細なコントロールをわざわざ作らないなら、気軽に使えるかもしれません。
指定したターム以下の階層かどうかを判定する関数
アーカイブの判定に使います。is_category や is_tax では指定したタームしか判定しませんが、こちらの関数を使うと指定したタームの階層以下すべてのタームを含めて判定します。。
たとえば arts > images > flyer と、階層があるカテゴリーがあり、現在のページが flyer のアーカイブを表示しようとしている場合、is_root_term(‘category’, ‘arts’ ) は true を返します。
テンプレートの中で、
if( is_root_term('category', ’arts') ) {
// ture の処理
}
を使用すると、カテゴリー arts, images, flyer すべてどれでも true になります。
is_category(‘arts’)では、’arts’のアーカイブしか true を返さず、images も flyer も arts 以下すべて含ませたいなら
if( is_category(array( 'arts', 'images', 'flyer' )) ) { }
と、全部書かねばなりません。
is_category を階層すべてに拡張し、taxonomy にも対応させた関数です。
is_root_term
is_root_term( $taxonomy, $term_slug )
引数
$taxonomy … タクソノミー。カテゴリーなら “category”
$term_slug … root というか、親にあたるタームスラッグを指定。これ以下の階層を全部含めて判定する。
関数
function is_root_term($taxonomy, $term_slug) { // 現在のページが指定されたタクソノミーのアーカイブページか、カテゴリのアーカイブページかをチェック if (!is_tax($taxonomy) && !is_category()) { return false; } // 現在表示されているタームを取得 $current_term = get_queried_object(); // ルートタームを取得 $root_term = get_term_by('slug', $term_slug, $taxonomy); if (!$root_term || is_wp_error($root_term)) { return false; } // ルートタームと現在のタームが一致する場合 if ($current_term->term_id == $root_term->term_id) { return true; } // 現在のタームの親をたどっていく $parent_id = $current_term->parent; while ($parent_id) { if ($parent_id == $root_term->term_id) { return true; } $parent_term = get_term($parent_id, $taxonomy); $parent_id = $parent_term->parent; } return false; }
解説
- 現在ページが何なのかをチェックしてタクソノミーのアーカイブでなければ関係ないのでfalseで終了。
- 現在表示のタームを調べてルートタームを取得
- 現在のタームがルートなら true を返して終了
- ルートじゃない場合、現在タームの親を辿り、該当したら true で終了、該当なしなら false で終了。
使いどころ
これはアーカイブ関連のテンプレートで使っています。一つは、archive.php の中で while して content を読み込むときに、その読み込む content.php を細かく振り分けます。
例えばカテゴリーのルートが event, articles, arts と大きな分類で、それぞれ子や孫のカテゴリーがあります。大分類ごとに読み込む content を指定するため、ルートを指定して条件分岐を作るんですね。
if( is_root_term('category', 'event') ){ // event関連のcontentテンプレートを読み込み }elseif( is_root_term('category', 'articles') ){ // 記事関連のcontentテンプレートを読み込み }elseif( is_root_term('category', 'arts') ){ // arts関連のcontentテンプレートを読み込み }
is_category を使うと、子や孫も全部事細かに書かねばなりませんがこちらの関数では親タームだけでざっくり振り分けできます。
以上、階層があるタームに関する4つの自作関数でした。このあとはオマケで、WordPress関数について補足。
WordPress 関数
以下はオマケのリファレンス的なやつです。メモとして。Codex もなくなってしまったことだし。
term_is_ancestor_of
term_is_ancestor_of、こんなの知りませんでした。子孫であるかどうかを調べて答える関数のようです。
引数は三つ。
$親term は、親のオブジェクトIDまたはターム情報(スラッグOK)
$子孫term は、子孫のオブジェクトIDまたはターム情報(スラッグOK)
そして $taxonomy です。
$子孫term が $親term の子孫である場合に true を返します。
新しい関数なのかと思ったら、WordPress 3.4 以降にすでにあったようで。
Codex がなくなって、本家のほうにはかろうじてリファレンス的なページが作られているようですが、めちゃ使いにくい上に見にくい上に情報も雑になって日本語もなくなってしまいましたねえ。
WordPress Developer Resouces – term_is_ancestor_of()
get_ancestors
指定オブジェクトの祖先を配列で返します。オブジェクトはタームに限らず、ページやカスタムポストなど階層があれば何にでも使えるようです。
<?php get_ancestors( $object_id, $object_type, $resource_type ); ?>
$object_id … オブジェクト(子側)のID
$object_type … 祖先オブジェクトの種類(page, category, 階層を持つpost_type, 階層を持つ taxonomy … )
$resource_type … $object_type の種類(post_type, taxonomy)省略すると自動判別
はて。object_type と resouce_type の使い分けがいまいち判りにくいが何でしょう。実際には、パラメータを二個入れれば十分っぽいので気にしないでおきます。
Codex に掲載されていた例
次のようなカテゴリーがあったとします。
6. 本 23. フィクション 108. SF 208. ミステリー 801. 温泉 1011. 電車 25. 実話
温泉、電車て(笑)いや、まあいいです。次のコードを実行します。
<?php $ancestors_ids = get_ancestors( 801, 'category' ); ?>
801の温泉カテゴリーの祖先が返りました。
Array ( [0] => 208 [1] => 23 [2] => 6 )
配列の最初が直属の親(ミステリー)、最後が最上位の親(本)の term_id になります。この順番は常にそうなりますので、最初の行、最後の行、と取得することで狙った結果を受け取れます。
配列の最初なら $term[0] という書き方でも取れますが、最後となるとその前に行を数えたりしなければなりませんから、array_shift(最初を抜き出す)array_pop(最後を抜き出す)を使えば簡単かなと思います。
get_the_terms
お馴染み get_the_terms ですが、実際にはどういう情報が返ってくるのか、いまいち判っていませんでした。
<?php get_the_terms( $id, $taxonomy ); ?>
パラメータ
$id … 必須の post ID
$taxonomy … タクソノミー
返り値
stdClass Object ( [term_id] => [name] => [slug] => [term_group] => [term_order] => [term_taxonomy_id] => [taxonomy] => [description] => [parent] => [count] => [object_id] => )
[parent] もあるんですね。[term_id] の直属の親があればここに 親 term_id が返されます。祖先を辿ることはありません。
<?php $term = get_the_terms( $post_id, 'category' ); ?>
こんなふうに変数に入れて以下のように好きなのを取り出して使います。
$term_id = $term->term_id; $name = $term->name; $slug = $term->slug; $parent = $term->parent;