会員ログイン
テーマ開発

WordPressでメニュー順にトップページのカテゴリーセクションを並べる方法

WordPressでメニュー順にトップページのカテゴリーセクションを並べる方法のアイキャッチ画像
はじめに
概要

この記事では、WordPressのヘッダーメニューに登録した親カテゴリーの順番を取得し、トップページのカテゴリーセクションを同じ順番で表示する方法を解説します。WebCantaで実際に使用しているwebcanta_top_categories()front-page.phpをもとに、管理画面での設定、PHPでの取得、記事カードの表示、並ばないときの確認点まで順番に整理します。

※本記事の情報は執筆時点のものです。WordPressやテーマの構成によって、管理画面の名称や必要なコードが異なる場合があります。
疑問を持つ読者のイラスト読者

トップページのカテゴリーセクションを、ヘッダーメニューと同じ順番にできますか?

著者・木村のプロフィール写真木村

できます。メニュー項目をPHPで取得し、親カテゴリーだけを順番どおりに取り出せば、その配列をトップページでも利用できます。

話を聞く読者のイラスト読者

カテゴリー側にも並び順がありそうですが、なぜメニューを基準にするのでしょうか?

著者・木村のプロフィール写真木村

管理画面の「外観 → メニュー」なら、ドラッグ操作だけで順番を変えられるからです。ヘッダーとトップページが同じ設定を参照するため、二重管理も防げます。

疑問を持つ読者のイラスト読者

メニューにお問い合わせなどの固定ページが入っていても大丈夫ですか?

著者・木村のプロフィール写真木村

問題ありません。カテゴリーであること、親階層であることを条件にしているため、固定ページや子カテゴリーは自動で除外されます。実際のコードを順番に確認しましょう。

メニュー順でカテゴリーセクションを並べる仕組み

結論からいうと、ヘッダーメニューから親カテゴリーの配列を作り、その配列を各表示場所で使い回します。WebCantaでは独自関数webcanta_top_categories()がこの役割を担当しています。

ヘッダーメニューを並び順の基準にする

WordPressのメニューは、管理画面で項目をドラッグして順番を変更できます。PHPでメニュー項目を取得すると、この画面で保存した順番のまま配列が返ります。

たとえば、ヘッダーメニューを「Shopify → HTML → CSS → JavaScript → WordPress」の順にすると、独自関数も同じ順でカテゴリーを返します。テンプレート側は、その返り値をforeachで処理するだけです。

1つの関数を複数の場所で使う

WebCantaでは、同じwebcanta_top_categories()を次の場所で使用しています。

  • トップページ上部のカテゴリーリンク
  • トップページのカテゴリー別セクション
  • サイドバーのカテゴリー一覧
  • フッターのカテゴリーリンク

それぞれのテンプレートへカテゴリー名を直接書かないため、メニューの順番を変えたときに関連箇所がまとめて更新されます。表示順の基準を1か所へ集めることが、この実装の重要なポイントです。

WordPress管理画面でヘッダーメニューを準備する

PHPを書く前に、テーマへメニュー位置を登録し、管理画面で親カテゴリーを並べます。ここはWordPress標準のメニュー機能を使う部分です。

テーマへheaderメニュー位置を登録する

まず、テーマのセットアップ処理でheaderというメニュー位置を登録します。WebCantaではPC用ヘッダー、モバイル、フッターの3か所を用意しています。

<?php
// テーマ内で使用するナビゲーションメニューの表示位置を登録する。
register_nav_menus(
  array(
    'header' => 'ヘッダーメニュー',
    'mobile' => 'モバイルメニュー',
    'footer' => 'フッターメニュー',
  )
);
?>

headerはプログラムで使う識別子です。後ほどget_nav_menu_locations()でこの識別子を指定し、割り当てられたメニューを取得します。

親カテゴリーをメニューへ追加する

管理画面の「外観 → メニュー」を開き、トップページへ表示したい親カテゴリーをメニューへ追加します。WebCantaではShopify、HTML、CSS、JavaScript、WordPress、制作ツール、その他を親カテゴリーとして登録しています。

固定ページやカスタムリンクが同じメニューに含まれていても構いません。後ほどPHP側で「カテゴリーだけ」に絞り込みます。

表示したい順に並べて保存する

追加したカテゴリーをドラッグし、トップページへ表示したい順番に並べます。最後に「メニュー設定」でヘッダーメニューの表示位置へ割り当て、メニューを保存します。

子カテゴリーをドロップダウンとして表示する場合は、親カテゴリーの下へ少し右にずらして配置します。この実装ではmenu_item_parentを確認するため、階層化した子項目はトップページの大きなセクションから除外されます。

ヘッダーメニューから親カテゴリーを取得する

ここからはテーマ独自実装です。メニュー位置から項目を取得し、トップ階層のカテゴリーだけをWP_Termオブジェクトとして配列へ追加します。

webcanta_top_categories()の全体

WebCantaのinc/content.phpでは、次の関数で親カテゴリーを取得しています。メニューが使える場合と、使えない場合の両方を考えた構成です。

<?php
/**
 * 親カテゴリーをヘッダーメニューの並び順で取得する。
 *
 * @return WP_Term[]
 */
function webcanta_top_categories() {
  $locations = get_nav_menu_locations();
  $ordered   = array();

  if ( ! empty( $locations['header'] ) ) {
    $menu_items = wp_get_nav_menu_items( $locations['header'] );

    foreach ( $menu_items ?: array() as $menu_item ) {
      if (
        0 !== (int) $menu_item->menu_item_parent
        || 'taxonomy' !== $menu_item->type
        || 'category' !== $menu_item->object
      ) {
        continue;
      }

      $term = get_term( (int) $menu_item->object_id, 'category' );
      if ( $term instanceof WP_Term && 0 === (int) $term->parent ) {
        $ordered[] = $term;
      }
    }
  }

  if ( ! $ordered ) {
    $ordered = get_categories(
      array(
        'parent'     => 0,
        'hide_empty' => false,
        'orderby'    => 'term_id',
        'order'      => 'ASC',
      )
    );
  }

  return apply_filters( 'webcanta_top_categories', $ordered );
}
?>

メニュー位置と項目を取得する

get_nav_menu_locations()は、テーマへ登録された各表示位置とメニューIDの対応を返すWordPress標準関数です。$locations['header']があれば、ヘッダー位置へメニューが割り当てられています。

wp_get_nav_menu_items()へメニューIDを渡すと、管理画面で保存した順番のメニュー項目を取得できます。ここで得られるのはカテゴリーだけではなく、固定ページやカスタムリンクも含むメニュー項目の一覧です。

トップ階層のカテゴリーだけを残す

foreach内の条件分岐では、次の3つを確認しています。

  • menu_item_parent0で、メニュー上のトップ階層にある
  • typetaxonomyで、タクソノミー項目である
  • objectcategoryで、投稿カテゴリーである

条件に合わない項目はcontinueで読み飛ばします。その後、get_term()でカテゴリー情報を取得し、カテゴリー自体も親階層であることを確認してから$orderedへ追加します。

メニュー上の階層とカテゴリー自体の階層を両方確認するため、子カテゴリーを誤ってトップページの大きなセクションとして表示しにくい実装です。

メニュー未設定時のフォールバックを用意する

ヘッダーメニューが未設定、または条件に合うカテゴリーが1件もない場合は、get_categories()で親カテゴリーを取得します。これがフォールバックです。

WebCantaではhide_emptyfalseにしているため、記事がまだない親カテゴリーも対象になります。ただし、トップページ側では記事取得後に空のセクションをスキップするため、結果として記事カードがないカテゴリーセクションは表示されません。

トップページへカテゴリーセクションを同じ順で表示する

親カテゴリーの配列を取得できたら、front-page.phpで繰り返し処理します。配列の順番を変更せずにforeachへ渡すため、セクションもメニューと同じ順番になります。

foreachでカテゴリーごとの処理を繰り返す

基本形は次のようになります。カテゴリー名と一覧ページへのリンクを、取得した順に出力します。

<?php foreach ( webcanta_top_categories() as $category ) : ?>
  <section class="section section--category">
    <h2><?php echo esc_html( $category->name ); ?></h2>
    <a href="<?php echo esc_url( get_category_link( $category ) ); ?>">
      もっと見る
    </a>
  </section>
<?php endforeach; ?>

表示時はesc_html()esc_url()でエスケープします。管理画面から取得した値でも、HTMLへ出力する場所に合ったエスケープを行うことが大切です。

WP_Queryでカテゴリーの記事カードを取得する

WebCantaでは、各親カテゴリーのセクション内へ記事カードを4件表示しています。tax_queryへ現在のカテゴリーIDを渡し、include_childrentrueにすることで、子カテゴリーの記事も含めます。

<?php
// トップページの各親カテゴリーへ、子カテゴリーを含む記事カードを表示する。
$category_posts = new WP_Query(
  array(
    'post_type'      => 'post',
    'posts_per_page' => 4,
    'orderby'        => 'rand',
    'no_found_rows'  => true,
    'tax_query'      => array(
      array(
        'taxonomy'         => 'category',
        'terms'            => $category->term_id,
        'include_children' => true,
      ),
    ),
  )
);

if ( ! $category_posts->have_posts() ) {
  continue;
}
?>

no_found_rowstrueにすると、ページネーションに必要な総件数を計算しません。トップページで数件だけ表示する用途では、不要な計算を減らせます。

orderbyrandは記事をランダム表示するWebCanta固有の方針です。新着順にしたい場合はdate、手動順を使う場合は要件に合わせた値へ変更してください。カテゴリーセクションの順番とは別の設定です。

記事がないカテゴリーはセクションごと省く

have_posts()falseの場合はcontinueで次のカテゴリーへ進みます。そのため、メニューには存在しても記事カードを取得できないカテゴリーは、空の見出しだけが残りません。

記事カードを出力した後は、必ずwp_reset_postdata()を実行します。カテゴリーごとに複数のWP_Queryを使うため、グローバルな投稿データを元へ戻しておく必要があります。

<div class="post-grid">
  <?php
  while ( $category_posts->have_posts() ) {
    $category_posts->the_post();
    webcanta_post_card();
  }
  wp_reset_postdata();
  ?>
</div>

子カテゴリーへのリンクも親ごとに表示する

WebCantaでは親カテゴリーの見出し下へ、子カテゴリーをチップ形式で表示しています。親カテゴリーの順番はヘッダーメニューから取得し、各親に属する子カテゴリーはwebcanta_child_categories()で取得する役割分担です。

ここで重要なのは、親セクションの並びと子リンクの並びを混同しないことです。トップページ全体の大きな順番はヘッダーメニュー、セクション内の小さな分類はカテゴリー階層が担当します。

メニュー順を使うメリットと注意点

メニュー順を使うと運用しやすくなりますが、メニュー項目なら何でもセクションになるわけではありません。実装の前提を理解しておくと、並びが合わないときも原因を見つけやすくなります。

管理画面だけで表示順を変更できる

最大のメリットは、PHPファイルを編集せずにカテゴリーセクションの順番を変えられることです。運用担当者は「外観 → メニュー」で項目をドラッグし、保存するだけで対応できます。

新しい親カテゴリーを増やす場合も、カテゴリー作成後にヘッダーメニューへ追加すれば取得対象になります。テンプレートへカテゴリーIDを追加する作業は不要です。

表示順を二重管理しなくてよい

ヘッダーはメニュー順、トップページはPHPの配列順という別々の設定にすると、更新時に片方だけ変更される可能性があります。同じ関数を共通利用すれば、表示場所が増えても基準はヘッダーメニューのままです。

同じ意味の順番は、できるだけ1つの設定から取得すると保守しやすくなります。

カスタムリンクや固定ページは対象外になる

この関数はtypeobjectを確認しているため、固定ページ、投稿、カスタムリンクは返しません。「お問い合わせ」をメニューへ置いても、トップページへお問い合わせセクションが自動追加されることはありません。

固定ページも含めて任意のセクションを並べたい場合は、カテゴリー専用の処理を広げる必要があります。ただし、取得後の表示内容も種類ごとに変わるため、まずカテゴリーだけを対象にした方が構造を保ちやすくなります。

制作メモ
メニューは「ナビゲーションの表示」と「トップページの並び順」の両方を担当します。役割が分かるように、関数名とコメントへ「ヘッダーメニュー順で取得する」と明記しておくと保守しやすくなります。

順番が合わないときの確認方法

コードを追加しても期待した順番にならない場合は、管理画面の割り当て、メニュー項目の種類、カテゴリーの記事有無を順に確認します。

headerの表示位置へメニューが割り当てられているか

最初に「外観 → メニュー → メニュー設定」を確認します。作成したメニューがヘッダーメニューの表示位置へ割り当てられていないと、$locations['header']を取得できません。

この場合はフォールバックが動き、カテゴリーID順になります。「カテゴリーは表示されるが、メニュー順ではない」という症状が出たときに確認したい項目です。

メニューへカテゴリーとして追加されているか

同じURLへ移動できても、カスタムリンクとして追加した項目はobject === 'category'になりません。管理画面のカテゴリー一覧からメニューへ追加してください。

また、メニュー上で別の項目の子になっている場合はmenu_item_parent0ではなくなります。トップセクションとして表示したいカテゴリーは、メニューの最上位へ配置します。

記事がないためセクションが省かれていないか

親カテゴリーは正しく取得できていても、WP_Queryで記事が見つからない場合はcontinueによってセクションが省かれます。対象カテゴリーまたは子カテゴリーへ公開済み記事があるか確認します。

下書き記事だけの場合、通常のフロント画面ではカードに含まれません。管理者としてログインした状態だけで判断せず、公開状態とカテゴリー設定を確認してください。

キャッシュが古い表示を残していないか

メニュー保存後も古い順番が表示される場合は、ページキャッシュ、CDN、ブラウザーキャッシュの順に確認します。まず管理画面でメニューが保存されていることを再確認し、その後で使用中のキャッシュ機能だけを対象に更新します。

コードを変更し続ける前に、キャッシュを介さないローカル環境やプレビューで取得順を確認すると、PHPの問題と表示キャッシュの問題を切り分けやすくなります。

まとめ

WordPressのメニュー順をトップページのカテゴリーセクションへ反映するポイントをまとめます。

  • ヘッダーメニューをカテゴリー表示順の基準にする
  • get_nav_menu_locations()headerのメニューIDを取得する
  • wp_get_nav_menu_items()で保存済みの順番どおりに項目を取得する
  • トップ階層の投稿カテゴリーだけを条件で残す
  • 取得した配列をfront-page.phpforeachでそのまま使う
  • メニュー未設定時のフォールバックと、記事がない場合の処理を用意する

この構成なら、運用時の並べ替えは管理画面だけで完了します。ヘッダー、トップページ、サイドバーなどが同じ関数を参照するため、カテゴリーの追加や順番変更にも対応しやすくなります。

MEMBER COMMENTS

コメント(0件)

会員ログイン後にコメントできます

記事への質問や感想をコメントで共有できます。無料会員登録後もこの記事へ戻れます。