グローバルナビメニューのカテゴリーに投稿数を表示する方法

Cocoonカスタマイズ集
この記事は約14分で読めます。
記事内に広告が含まれています。

完成イメージ

実装手順

以下のコードをfunctions.phpに追加します。

add_filter('wp_nav_menu_objects', function ($items, $args) {
  // 対象のメニュー位置
  $target_locations = [
    'navi-header',          // ヘッダーメニュー
    'navi-mobile',          // ヘッダーモバイルメニュー
    'navi-header-mobile',   // ヘッダーモバイルボタン
    'navi-footer',          // フッターメニュー
    'navi-footer-mobile',   // フッターモバイルボタン
    'navi-mobile-slide-in', // モバイルスライドインメニュー
  ];

  // 対象外の位置の場合
  if (empty($args->theme_location) || !in_array($args->theme_location, $target_locations, true)) {
    return $items;
  }

  foreach ($items as $item) {
    if ($item->object === 'category') {
      $category = get_category($item->object_id);
      if (isset($category->count)) {
        $item->title .= sprintf(' (%d)', $category->count);
      }
    }
  }

  return $items;
}, 10, 2);

補足

メニューも投稿の一部として扱われ、データベースのwp_postmetaの拡張で情報が管理されます。
表1にメニューの情報を示します。

wp_postmeta
キー内容
_menu_item_type

  • post_type=投稿、固定ページ

  • custom=カスタムリンク

  • taxonomy=カテゴリー、タグ

_menu_item_menu_item_parent1=親メニュー
_menu_item_object_idオブジェクトID
_menu_item_object

  • post=投稿

  • page=固定ページ

  • category=カテゴリー

  • post_tag=タグ

  • custom=カスタムリンク

_menu_item_target1=_blank
_menu_item_classesCSS class
_menu_item_xfn
_menu_item_urlカスタムリンクのURL
array(8) {
  ["_menu_item_type"]=>array(1) {
    [0]=>string(9) "post_type"
  }
  ["_menu_item_menu_item_parent"]=>array(1) {
    [0]=>string(1) "0"
  }
  ["_menu_item_object_id"]=>array(1) {
    [0]=>string(4) "7949"
  }
  ["_menu_item_object"]=>array(1) {
    [0]=>string(4) "page"
  }
  ["_menu_item_target"]=>array(1) {
    [0]=>string(6) "_blank"
  }
  ["_menu_item_classes"]=>array(1) {
    [0]=>string(22) "a:1:{i:0;s:5:"class";}"
  }
  ["_menu_item_xfn"]=>array(1) {
    [0]=> string(0) ""
  }
  ["_menu_item_url"]=>array(1) {
    [0]=>string(0) ""
  }
}

参考:ボックスメニューのカテゴリーに投稿数を表示

以下のメニューにも投稿数を表示します。

  • ボックスメニュー
  • おすすめカード
  • ナビカード

完成イメージ

実装手順

以下のコードをfunctions.phpに追加します。

add_filter('wp_get_nav_menu_items', function ($items, $menu, $args) {
  // 対象とする関数名
  $target_functions = [
    'get_box_menu_tag',       // ボックスメニュー
    'get_navi_card_list_tag', // おすすめカード、ナビカード
  ];

  // 呼び出し元関数名の配列
  $caller_functions = array_column(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 10), 'function');

  // 対象関数が呼び出し元に含まれない場合
  if (empty(array_intersect($target_functions, $caller_functions))) {
    return $items;
  }

  foreach ($items as $item) {
    // メニューアイテムがカテゴリーの場合
    if ($item->object === 'category') {
      $category = get_category($item->object_id);
      if (isset($category->count)) {
        $item->title .= sprintf(' (%d)', $category->count);
      }
    }
  }

  return $items;
}, 10, 3);

補足

WordPressで特定コンテキスト(ボックスメニューやナビカード等)だけに処理を適用したい場合、大きく分けてバックトレース方式とショートコードフック方式の2つが存在します。

それぞれ明確なメリット・デメリットや対応範囲の違いがあります(表2参照)。

両方式の比較
項目バックトレース方式
(debug_backtrace)
ショートコードフック方式
(pre/do_shortcode_tag)
コード構成フック1つでコンパクトに完結開始/終了フックの2箇所が必要
ウィジェット対応 完全対応 非対応
保守性 テーマ内部の関数名変更で動作停止リスク 公開ショートコード名指定のため変更リスク低
処理コストスタックトレースを展開するためわずかに重い 必要な瞬間だけフック登録するため超軽量

ショートコードフック方式のコードを以下に示します。

// 対象ショートコードの実行直前に実行
add_filter('pre_do_shortcode_tag', function ($output, $tag) {
  if (in_array($tag, get_category_count_target_shortcodes(), true)) {
    // フィルターを追加
    add_filter('wp_get_nav_menu_items', 'add_category_count_to_menu_items', 10);
  }
  return $output;
}, 10, 2);


// 対象ショートコードの実行完了直後に実行
add_filter('do_shortcode_tag', function ($output, $tag) {
  if (in_array($tag, get_category_count_target_shortcodes(), true)) {
    // 追加したフィルターを解除
    remove_filter('wp_get_nav_menu_items', 'add_category_count_to_menu_items', 10);
  }
  return $output;
}, 10, 2);


/**
 * 件数追加の対象とするショートコード名一覧
 * 
 * @return array
 */
function get_category_count_target_shortcodes() {
  return [
    'box_menu',   // ボックスメニュー
    'navi_list',  // ナビカード、おすすめカード
  ];
}


/**
 * メニューアイテムがカテゴリーの場合に記事数をタイトルへ追加
 *
 * @param array $items メニューアイテムの配列
 * @return array 記事数が追加されたメニューアイテムの配列
 */
function add_category_count_to_menu_items($items) {
  foreach ($items as $item) {
    if ($item->object === 'category') {
      $category = get_category($item->object_id);
      if (isset($category->count)) {
        $item->title .= sprintf(' (%d)', $category->count);
      }
    }
  }

  return $items;
}
タイトルとURLをコピーしました