みなさん、WordPressでサイドバーにカテゴリ一覧を作ってますか?

このブログでは get_terms() を使って全カテゴリを配列で取得し、 foreach でループを作って処理してるよ。

ウィジェットとかじゃなくて、自分で作ればどんな表示も自由自在だね。

例えば、そのカテゴリに属する記事の数を表示したり……

この数の数え方なんだけど、子孫カテゴリに属する記事も含めるかどうかはけっこう悩みどころ。

含めたい場合に get_terms() で使うのが、引数の pad_counts というキーだ。

ところが、この pad_counts には不可解な挙動があり、思うようにカウントされない場合がある。

今回は、そんな pad_counts の罠と、その対処法について紹介するよ。

get_terms() だけでなく get_categories() でも同様。

カウントが合計されない

カテゴリアーカイブのページでは子孫カテゴリに属する記事も表示されるから、俺はサイドバーのカテゴリ一覧でも子孫カテゴリの分をカウントに入れてるんだよね。

その場合の全カテゴリの取得方法は以下のような感じ。

$all_cats = get_terms(array(
    'taxonomy'   => 'category',
    'hide_empty' => false,
    'orderby'    => 'description',
    'pad_counts' => true,
    'parent'     => 0,
));

'hide_empty' => false により、記事のないカテゴリもとりあえず取得。

'orderby' => 'description' (カテゴリの説明文)という変わった並び順にしているのは、説明文を通し番号として、自由な並び順を実現しているため。詳しくは過去の記事で。

'pad_counts' => true で、取得したカテゴリの記事数は、その子カテゴリ以下に属する記事の数も含むようになる。

'parent' => 0 によって、最上位の階層のカテゴリのみを取得。

子カテゴリはループ処理内で、子カテゴリを持つ場合に取得し、さらにループを組んで処理。

残念ながら孫カテゴリ以下には対応させてないけど、このブログには今のところ存在しないし、いいよね。

さて、これでループを組めば……

foreach ( $all_cats as $cat ) {
    echo '<li><a href="'.esc_url(get_category_link($cat)).'">'.$cat->name.'</a> ('.$cat->count.')</li>'."\n";
    ......
}

$cat->count が記事数だから、これで……

……あれっ??? やけに少ないぞ?

pad_counts の罠

検証してみると $cat->count には、そのカテゴリ自体に属する記事の数しか含まれておらず、子カテゴリの分が含まれていない!

引数の pad_counts を指定しているのに、なぜだ……

そんなときは調べるに限る。

……特にそれらしいことは書いてない、間違ってることはなさそうなのに。

一応英語版も読んでみたが、追加情報はなかった。

そこでさらに調べると、以下のページで解決した。

このページによると、引数の pad_counts を使うと内部的に _pad_term_counts() を呼び出すが、この関数は取得したターム(カテゴリ)のすべての子孫が同時に取得されていることを前提としている、という。

つまり、引数の parent なんかを使い、子孫カテゴリを取得していない場合、 pad_counts は正常に働かない、ということだ!

\ナ、ナンダッテー/

parent の使用を回避する

この問題を解決するには、子孫カテゴリも含めたすべてのカテゴリを一旦取得してしまうほかあるまい。

その上で、ループ処理内で親カテゴリを持つカテゴリを弾いちゃえばおk。

ということで取得。

$all_cats = get_terms(array(
    'taxonomy'   => 'category',
    'hide_empty' => false,
    'orderby'    => 'description',
    'pad_counts' => true,
    //'parent' => 0 は使わない
));

そしてループ処理。

foreach ( $all_cats as $cat ) {
    if ( $cat->parent )
        continue;
    echo '<li><a href="'.esc_url(get_category_link($cat)).'">'.$cat->name.'</a> ('.$cat->count.')</li>'."\n";
    ......
}

ちなみに、子カテゴリはこのループ処理内で get_terms() の引数の parent$cat->term_id として取得する。

まとめ

  • pad_countsparent と同時に指定するとうまくカウントできないよ。
  • pad_counts を指定する時は子孫カテゴリも一緒に取得するようにしよう。
  • 処理するカテゴリを絞り込みたい場合はループ処理内で。

以上を守って楽しくWordPress!

……じゃなくて、お役に立てば幸いです。