タームの親子を取得する方法|parent・child_of・祖先の違い

結論

階層型タクソノミー(カテゴリーのように親子を持てるもの)で関係するタームを取るときは、目的別に次のように使い分けます。

やりたいこと 使うもの
直下の子だけ 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> &raquo; ', 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 に入れるより、先にタームの構造を見直すほうが効果的な場合も多いと思います。

タームの親子を取得する方法の関連項目

出典(一次情報)

タイトルとURLをコピーしました