WordPress の 階層がある taxonomy で親や最上位祖先を取得したり、条件分岐で使える関数を作ったりします。
今宵は親タームや最上位タームを取得したいという話です。もう一つ、親タームの指定で条件分岐したいという話で、関数を作ります。
category 関連の関数を使ってマイ関数を作ったりしていましたが、これをtaxonomy全般に置き換えようとして、ちょっと考え込んだりしました。Codex を見て仕様を確認しようと思ったら、すでに消滅していてガッカリ。結局、魚拓系のInternet Archive で見つけ出して何とか確認できたので、昔のコードを書き直すことができました。
例として、次のような階層付きのカテゴリーがあるとします。
- media
- photos
- event_photos
- promotion
- videos
- pv
- performance
- images
- flyer
- coverart
- photos
カテゴリーには「media」以下、階層があります。投稿には主に末端カテゴリーがひとつだけセットされます。fiyer だとか pv とかですね。✓media ✓images ✓flyer と、階層を辿るカテゴリーをすべてセットすることはありません。そういう前提です。
例えば、カテゴリー flyer がセットされているとき、階層1個上の親「images」とか、階層最上位、祖先トップの親「media」の、名前やスラッグを取得したいと。そういうことがよくあります。
階層一個上の親タームや最上位の親タームを取得
親の情報を取得できるのは get_ancestors 関数ですので、これを使うんですが便利関数を作りたいので他の一手間をまとめておくというのが目的で趣旨です。
最上位の親をゲットする関数
post に、例えばカテゴリー flyer がセットされているとき、最上位の親「media」のスラッグまたは名前を取得します。
get_the_root_term というそれっぽい名前の関数を作ってみました。
関数
<?php // 最上位を取得 function get_the_root_term( $taxonomy, $nameOrSlug, $post_id = 0 ) { $term = array_shift( get_the_terms( $post_id, $taxonomy ) ); $root_id = array_pop( get_ancestors( $term->term_id, $taxonomy ) ); $slug = get_term( $root_id, $taxonomy )->slug; $name = get_term( $root_id, $taxonomy )->name; if($nameOrSlug == 'name' ){ return $name; }else{ return $slug; } }
親が存在しない場合エラー出るか?と思ったけど、出ないみたい。なので特に親IDの有無をチェックする if を入れていません。
使用
get_the_root_term( ) の引数は2個。タクソノミー名と、最終ほしい情報 ‘slug’ または ‘name’ を書きます。
現在セットされているカテゴリーから最上位の親の名前がほしい場合ならこうです。
get_the_root_term( 'category', 'name' );
2個目のパラメータは、何も指定しなければ slug を得るようにしていますが、完全に省略するのは駄目で、( ‘taxonomy名’, ” ) と書かないとエラー出ます。
解説
関数の中身はは次のような流れになってます。
- get_the_terms で、postにセットされた指定taxのターム情報をゲット
- ゲットした最初の1個(セットされたタームそのもの)を抜き出して
- get_ancestors にそのIDを指定して親IDすべてのリストをゲット
- 取得した親IDリストの最初が、親です
- 取得した親IDリストの最後が、最上位の親です
- どの親か特定してそのIDを元に get_term でターム情報を取得
- get_term から、スラッグや名前などを取り出す
コードの中で以下の部分、
$root_id = array_pop( get_ancestors( $term->term_id, $taxonomy )
ここで、最上位の親をゲットしています。array_pop(最後のアイテム)を array_shift(最初のアイテム)に変えてやると、最初の親つまり直属の親をゲットできます。
親をゲットする関数
その一箇所だけ変えた「親ターム取得」の関数ならこんな感じで。get_the_parent_term という、これまたそれっぽい名前です。
// 親を取得 function get_the_parent_term( $taxonomy, $nameOrSlug, $post_id = 0 ) { $term = array_shift( get_the_terms( $post_id, $taxonomy ) ); $parent_id = array_shift( get_ancestors( $term->term_id, $taxonomy ) ); $slug = get_term( $parent_id, $taxonomy )->slug; $name = get_term( $parent_id, $taxonomy )->name; if($nameOrSlug == 'name' ){ return $name; }else{ return $slug; } }
$post_id について
$post_id がすでにあるものとしているのでループの中で使いますが、そうでない場合はPHPバージョン8以降、勝手に $post_id を書けばエラーで叱られます。
if (!$post_id) { $post_id = get_the_ID(); }
あるいは
global $post; $post_id = $post->ID
最初にこういうのを入れておくと叱られずに済むかもしれません。
親タームで条件分岐する
カテゴリーやタクソノミーで分岐しようとするとき、子孫があると、条件に全部のタームを書かねばなりません。
is_category( array(‘images’,’photos’,’event_photo’,’promotion’… ) こんな風に、全部のカテゴリーを羅列しないといけませんね。こんな面倒はいやだ。
has_parent_term ( ‘category’, ‘photos’ ) こんなふうに書くだけで photos 以下の子孫が条件に合致してほしい。ということで has_parent_term というかっこいい名前のそれっぽい関数を作ります。
上記の親タームと最上位タームを利用して論理値が返るように書き加えて条件分岐すればいいだけの気もしますが、条件分岐専用の関数を作っておくのも良いかもしれません。良いのかな。いらんか?わかんないですけど、かつて作っていましたので、話の流れ上、別の関数として書いときます。
親タームで条件分岐
親のタームを指定すれば自動的にその子孫も含めて条件に合致するかどうかを答える関数です。
関数
function has_parent_term($taxonomy, $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 ($term->slug == $term_slug || term_is_ancestor_of($term_slug, $term, $taxonomy)) { return true; } } return false; }
使用
has_parent_term( $taxonomy, $term_slug ) です。二つの引数は、タクソノミー名と目的の親タームスラッグです。三つ目の post_id もありますが「このポスト」前提ですので手動で何か書く必要はありません。
条件分岐で使用するときは「もし指定ターム、または、指定タームの子孫なら」というふうに書きます。
if( is_category('images') || has_root_term('category','images') ) { // 処理 }
この例ではカテゴリーが「images」または「images」の子孫である場合に条件に合致します。テンプレートを弄っているとこういう条件分岐を頻繁に使いますよね(is_category の部分、taxonomy なら is_tax )
解説
最初に $post_id について念押ししてから、wp_get_post_terms で terms をゲットしています。
wp_get_post_terms が get_the_terms とどう違うのか、あまり判っていません。どこが違うんでしょう?wp_get_post_terms では並び順を指定できたりしますか。表示を前提としているとき以外は関係ないですね。まあなんかわからんけど愛嬌で wp_get_post_terms を使っていますが気にしないでください。
取得した terms をループして、それぞれ term のスラッグを term_is_ancestor_of 関数に投げ入れ、指定タームの子孫かどうかを判断します。
最終 true か false さえ判ればいいので、個別にスラッグを取得したりごちゃごちゃする必要もなく、条件分岐用のこの関数、作っておいて損はないかなと思います。
archive.php とか、そういうテンプレートでこの手の条件分岐を頻繁に使います。テンプレート階層を利用して無駄にファイルを増やすより、分岐で済ませるのが近年の好みです。
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;
なんか唐突にWordPressの関数のお話でした。