WordPressのパンくず表示修正依頼で少しハマった話

WordPressのパンくず表示修正依頼で少しハマった話

「投稿ページのパンくず表示が上手くいかないので修正してもらえませんか?」

このような依頼があり、色々確認させてもらうと以下のようになっていました。

  • カテゴリーの構造が「親 > 子 > 孫」
  • カテゴリーの構造は運営途中で変更
  • 上手く「親 > 子 > 孫」で表示されるページもある
  • 投稿ページのカテゴリーは「親子孫」すべて設定
目次

「親 > 子 > 孫」で表示されない理由

依頼者が使用しているテーマでは、投稿ページのパンくず表示を以下のような方法で行っていました。

$category = [];
$categories = get_the_category($post->ID);
$cat = $categories[0];
if ($cat -> parent != 0) {
  $ancestors = array_reverse(get_ancestors($cat->cat_ID, 'category'));
  foreach ($ancestors as $index => $ancestor) {
    $category[] = array(
      'url' => get_category_link($ancestor),
      'name' => get_cat_name($ancestor)
    );
  }
}
$category[] = array(
  'url' => get_category_link($cat->term_id),
  'name' => $cat->cat_name
);

これは、パンくず表示用のソースコードの一部ですが、この中に原因があります。

WordPressは「親 > 子 > 孫」というカテゴリー階層になっている場合、孫カテゴリーだけを設定しておけば、「子カテゴリー一覧」「親カテゴリー一覧」それぞれに記事が表示される仕様になっています。

ただ、孫カテゴリーだけを設定した場合、親カテゴリーや子カテゴリーで記事数を取得すると孫カテゴリーだけを設定した記事はカウントされないという現象が起こります。

ただ、「親・子・孫」とすべてのカテゴリーを設定した場合、以下の部分が少し問題となってきます。

if ($cat -> parent != 0) {
  $ancestors = array_reverse(get_ancestors($cat->cat_ID, 'category'));
  foreach ($ancestors as $index => $ancestor) {
    $category[] = array(
      'url' => get_category_link($ancestor),
      'name' => get_cat_name($ancestor)
    );
  }
}
$category[] = array(
  'url' => get_category_link($cat->term_id),
  'name' => $cat->cat_name
);

get_the_categoryの特徴

get_the_category()は投稿データのカテゴリー情報を取得するWordpressの関数です。

この関数を使って、投稿ページに紐づけられているカテゴリーを取得できるのですが、「親・子・孫」すべてのカテゴリーを設定していた場合、すべてのカテゴリー情報が取得されます。

ここが一番重要なポイントなのですが、紐づけられているカテゴリーの取得順がスラッグ(名前)順となっているのです。

これを踏まえた上で、先ほど問題となる箇所として挙げたソースコードを確認すると、get_the_category()で取得したカテゴリーデータの一番目のカテゴリーデータを$catに格納しています。

その次に、一番目のカテゴリーに「親カテゴリーが存在するかどうか」を確認し、存在する場合はget_ancestors()という関数を使って、先祖を遡って、それぞれのカテゴリーIDを取得するという流れになっています。

つまり、スラッグ順で取得されたカテゴリーデータの一番目が「孫カテゴリー」であればいいのですが、親もしくは子カテゴリーだった場合、パンくずが期待した通りに表示されないという現象が起こってしまうということになります。

カテゴリーを「親 > 子 > 孫」で表示するには

「親 > 子 > 孫」の順で表示するとなると以下のように修正が必要です。

$category = [];
$categories = get_the_category($post->ID);
$cat = $categories[0];
$count = count($categories);

if($count == 1){
  if ($cat -> parent != 0) {
    $ancestors = array_reverse(get_ancestors($cat->cat_ID, 'category'));
    foreach ($ancestors as $index => $ancestor) {
      $category[] = array(
        'url' => get_category_link($ancestor),
        'name' => get_cat_name($ancestor)
      );
    }
  }
  $category[] = array(
    'url' => get_category_link($cat->term_id),
    'name' => $cat->cat_name
  );
}else{
  if ($cat->parent != 0) {
    $ancestor_id = array_pop(get_ancestors($cat->cat_ID,'category'));
  }else{
    $ancestor_id = $cat->cat_ID;
  }

  $children = get_term_children($ancestor_id,'category');
  if(!is_wp_error($children)){
    $category[] = array(
      'url' => get_category_link($ancestor_id),
      'name' => get_cat_name($ancestor_id)
    );
    foreach($children as $child){
      $category[] = array(
        'url' => get_category_link($child),
        'name' => get_cat_name($child)
      );
    }
  }else{
    $category[] = array(
      'url' => get_category_link($ancestor_id),
      'name' => get_cat_name($ancestor_id)
    );
  }
}

パートごとで何をしているか解説します。

まず、get_the_category()で投稿に紐づけられたカテゴリーを取得します。そして、count()を使い、配列の要素数を調べます。

  • 「孫カテゴリーのみの設定」なら「1」
  • 「親子孫カテゴリーすべて設定」なら「3」

$countが「1」であれば、元々テーマに書かれていたソースコードで「親 > 子 > 孫」といった順でパンくずが表示されます。

$countが「3」であった場合は次のように処理していきます。

一番上のカテゴリーを見つける

if ($cat->parent != 0) {
  $ancestor_id = array_pop(get_ancestors($cat->cat_ID,'category'));
}else{
  $ancestor_id = $cat->cat_ID;
}

まず、get_the_category()で取得した一番目のカテゴリーに親カテゴリーがあるかどうかを$cat->parentで確認します。

親が存在すれば「親カテゴリーのID」存在しなければ「0」が返されます。

ここで、親が存在すればget_ancestors()を使って一番上の親までのカテゴリーIDを取得します。

get_ancestors()は「孫 > 子 > 親」の順番でIDを返してくるので、array_pop()で一番最後の値を取り出し、$ancestor_idに値を代入します。

親カテゴリーが存在しない場合は、そのカテゴリーが親になるので、$ancestor_idにそのままカテゴリーIDを代入します。

これで、一番上の親カテゴリーが判明しました。

子・孫カテゴリーの存在を確認する

続いて、一番上の親カテゴリーが、子・孫カテゴリーを持っているかを確認して配列に入れていきます。

$children = get_term_children($ancestor_id,'category');
if(!is_wp_error($children)){
  $category[] = array(
    'url' => get_category_link($ancestor_id),
    'name' => get_cat_name($ancestor_id)
  );
  foreach($children as $child){
    $category[] = array(
      'url' => get_category_link($child),
      'name' => get_cat_name($child)
    );
  }
}else{
  $category[] = array(
    'url' => get_category_link($ancestor_id),
    'name' => get_cat_name($ancestor_id)
  );
}

まず、get_term_children()を使って子・孫カテゴリーの存在を確認します。

子カテゴリーがある場合は、子・孫それぞれのカテゴリーIDを配列で返してくるのですが、子・孫カテゴリーが存在しない場合は「WP_Errorオブジェクト」を返してくるので、is_wp_error()で分岐します。

また、get_term_children()は子・孫カテゴリーのIDしか返してこないので、$categryに親カテゴリーのデータを配列で格納します。

そのあとは、foreach()で子・孫それそれのカテゴリーデータを$categryに格納すれば「親 > 子 > 孫」の順でカテゴリーデータの取得が完了です。

この方法を使えば、パンくずだけでなく、JSON-LD形式のパンくずリスト構造化データなんかも記述できます。JSON-LD形式はGoogleも推奨しているので、書き方等はまた別の機会に紹介できればと思います。