結論
階層型タクソノミー(カテゴリーのように親子を持てるもの)で関係するタームを取るときは、目的別に次のように使い分けます。
| やりたいこと | 使うもの |
|---|---|
| 直下の子だけ | get_terms( array( 'parent' => $id ) ) |
| 孫以下を含む全子孫(ターム オブジェクト) | get_terms( array( 'child_of' => $id ) ) |
| 全子孫の ID 配列 | get_term_children( $id, $taxonomy ) |
| 祖先(親・祖父…)の ID | get_ancestors( $id, $taxonomy, 'taxonomy' ) |
| 最上位だけ | get_terms( array( 'parent' => 0 ) ) |
特に parent は直下のみ、child_of は全子孫 という違いが混同されやすい点です。
原因・仕組み
ターム同士の関係は term_taxonomy テーブルの parent 列に、親タームの ID として保存されます。親が無い最上位は0です。get_term_children() は、公式リファレンスによると「すべての子タームの ID を1つの配列にまとめて返す」関数で、階層型タクソノミーでのみ機能します。タクソノミーが存在しないと WP_Error が、子が無ければ空配列が返ります。
get_ancestors() は逆向きで、近い順(親、祖父…)に ID を並べた配列を返します。
手順(サンプルコード)
直下の子を一覧にする例です。
$children = get_terms( array(
'taxonomy' => 'genre',
'parent' => $parent_id,
'hide_empty' => false,
) );
if ( ! is_wp_error( $children ) && $children ) {
echo '<ul>';
foreach ( $children as $c ) {
printf( '<li><a href="%s">%s</a></li>',
esc_url( get_term_link( $c ) ), esc_html( $c->name ) );
}
echo '</ul>';
}
パンくず用に祖先を取り出す例です。
$ids = array_reverse( get_ancestors( $term_id, 'genre', 'taxonomy' ) ); // 上位から順に
foreach ( $ids as $id ) {
$t = get_term( $id, 'genre' );
if ( $t && ! is_wp_error( $t ) ) {
printf( '<a href="%s">%s</a> » ', esc_url( get_term_link( $t ) ), esc_html( $t->name ) );
}
}
$self = get_term( $term_id, 'genre' );
echo esc_html( $self->name );
投稿に付いたタームの中から、最下層のものだけを選ぶには、子を持つものを除外します。
$terms = wp_get_object_terms( $post_id, 'genre' );
$leaf = array_filter( $terms, function ( $t ) {
return empty( get_term_children( $t->term_id, 'genre' ) );
} );
動作確認(検証環境と結果)
WordPress 7.1.2(ja)/PHP 8.2.12/MariaDB 10.4.32。music(ID 4) > rock(5) > punk(6) と最上位の empty-top(7) を作って実行しました(tests フォルダの p4.php)。
parent=0: empty-top,music
parent=music (direct children): rock
child_of=music (all descendants): rock,punk
get_term_children(music): [5,6]
get_ancestors(punk): [5,4]
wp_get_object_terms: punk
parentは直下の rock だけ、child_ofは rock と punk の両方を返しました。get_ancestorsは近い順(親の rock=5、祖父の music=4)でした。パンくずではarray_reverseが必要です。- 投稿に punk だけを付けた場合、
wp_get_object_termsは punk だけを返し、親の rock・music は自動で付きませんでした。
タームの親子を取得する方法の注意点
- 非階層型(タグのような)タクソノミーでは親子の指定は無効です。登録時に
hierarchical => trueが必要です。 - 親を変更すると、子の URL(
hierarchicalなリライト設定のとき)も変わります。 child_ofは階層全体をたどるため、ターム数が多いサイトではキャッシュされる点も含めて計測します。get_term_children/get_ancestorsは ID 配列を返します。名前やリンクが必要ならget_term()で取り直します。- 投稿に親タームを自動で付けたい場合は、保存時に
get_ancestorsで祖先を求め、wp_set_object_termsで追加する実装が必要です(標準では付きません)。
タームの親子を取得する方法でよくあるミス
child_ofとparentを同時に指定して、結果が空になる。- 非階層タクソノミーに
get_term_childrenを使い、期待した結果にならない。 get_ancestorsの第3引数('taxonomy')を忘れ、投稿タイプとして処理されて空になる。- ループの中で毎回
get_term_childrenを呼び、クエリが増える。
タームの親子を取得する方法のチェックリスト
- [ ] 対象が
hierarchical => trueのタクソノミーである - [ ] 直下だけか、全子孫かを決めて
parent/child_ofを選んだ - [ ]
hide_emptyの扱いを決めている - [ ] 戻り値の
WP_Errorを確認している - [ ] 祖先は上位から表示するために順序を反転している
タームの親子を取得する方法のFAQ(よくある質問)
Q. 親の件数に子の投稿数を含めたい。
A. count は自身に直接付いた投稿の数です。合計を出すには、子を含めた tax_query(include_children)で件数を数えます。
Q. 子ごとに親を持たせるにはどうするか。
A. wp_insert_term の parent 引数に親の ID を渡します。
Q. 深い階層でも動きますか。
A. はい。ただし階層が深いほどクエリとキャッシュのコストは増えます。
筆者の見解(タームの親子を取得する方法)
親子関係の取得は、まず「直下か全子孫か」を決めるだけで、選ぶ関数がほぼ決まると考えています。私見では、パンくずや階層メニューのように繰り返し使う処理は、取得結果を transient に入れるより、先にタームの構造を見直すほうが効果的な場合も多いと思います。
タームの親子を取得する方法の関連項目
- get_terms() の使い方|説明・引数・注意点
- get_term() の使い方|説明・引数・注意点
- wp_insert_term() の使い方|説明・引数・注意点
- wp_get_post_terms() の使い方|説明・引数・注意点
- WP_Tax_Query クラスの使い方|説明・引数・注意点
