読者トップページのカテゴリーセクションを、ヘッダーメニューと同じ順番にできますか?
木村できます。メニュー項目を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_parentが0で、メニュー上のトップ階層にあるtypeがtaxonomyで、タクソノミー項目であるobjectがcategoryで、投稿カテゴリーである
条件に合わない項目はcontinueで読み飛ばします。その後、get_term()でカテゴリー情報を取得し、カテゴリー自体も親階層であることを確認してから$orderedへ追加します。
メニュー上の階層とカテゴリー自体の階層を両方確認するため、子カテゴリーを誤ってトップページの大きなセクションとして表示しにくい実装です。
メニュー未設定時のフォールバックを用意する
ヘッダーメニューが未設定、または条件に合うカテゴリーが1件もない場合は、get_categories()で親カテゴリーを取得します。これがフォールバックです。
WebCantaではhide_emptyをfalseにしているため、記事がまだない親カテゴリーも対象になります。ただし、トップページ側では記事取得後に空のセクションをスキップするため、結果として記事カードがないカテゴリーセクションは表示されません。
トップページへカテゴリーセクションを同じ順で表示する
親カテゴリーの配列を取得できたら、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_childrenをtrueにすることで、子カテゴリーの記事も含めます。
<?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_rowsをtrueにすると、ページネーションに必要な総件数を計算しません。トップページで数件だけ表示する用途では、不要な計算を減らせます。
orderbyのrandは記事をランダム表示する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つの設定から取得すると保守しやすくなります。
カスタムリンクや固定ページは対象外になる
この関数はtypeとobjectを確認しているため、固定ページ、投稿、カスタムリンクは返しません。「お問い合わせ」をメニューへ置いても、トップページへお問い合わせセクションが自動追加されることはありません。
固定ページも含めて任意のセクションを並べたい場合は、カテゴリー専用の処理を広げる必要があります。ただし、取得後の表示内容も種類ごとに変わるため、まずカテゴリーだけを対象にした方が構造を保ちやすくなります。
順番が合わないときの確認方法
コードを追加しても期待した順番にならない場合は、管理画面の割り当て、メニュー項目の種類、カテゴリーの記事有無を順に確認します。
headerの表示位置へメニューが割り当てられているか
最初に「外観 → メニュー → メニュー設定」を確認します。作成したメニューがヘッダーメニューの表示位置へ割り当てられていないと、$locations['header']を取得できません。
この場合はフォールバックが動き、カテゴリーID順になります。「カテゴリーは表示されるが、メニュー順ではない」という症状が出たときに確認したい項目です。
メニューへカテゴリーとして追加されているか
同じURLへ移動できても、カスタムリンクとして追加した項目はobject === 'category'になりません。管理画面のカテゴリー一覧からメニューへ追加してください。
また、メニュー上で別の項目の子になっている場合はmenu_item_parentが0ではなくなります。トップセクションとして表示したいカテゴリーは、メニューの最上位へ配置します。
記事がないためセクションが省かれていないか
親カテゴリーは正しく取得できていても、WP_Queryで記事が見つからない場合はcontinueによってセクションが省かれます。対象カテゴリーまたは子カテゴリーへ公開済み記事があるか確認します。
下書き記事だけの場合、通常のフロント画面ではカードに含まれません。管理者としてログインした状態だけで判断せず、公開状態とカテゴリー設定を確認してください。
キャッシュが古い表示を残していないか
メニュー保存後も古い順番が表示される場合は、ページキャッシュ、CDN、ブラウザーキャッシュの順に確認します。まず管理画面でメニューが保存されていることを再確認し、その後で使用中のキャッシュ機能だけを対象に更新します。
コードを変更し続ける前に、キャッシュを介さないローカル環境やプレビューで取得順を確認すると、PHPの問題と表示キャッシュの問題を切り分けやすくなります。
まとめ
WordPressのメニュー順をトップページのカテゴリーセクションへ反映するポイントをまとめます。
- ヘッダーメニューをカテゴリー表示順の基準にする
get_nav_menu_locations()でheaderのメニューIDを取得するwp_get_nav_menu_items()で保存済みの順番どおりに項目を取得する- トップ階層の投稿カテゴリーだけを条件で残す
- 取得した配列を
front-page.phpのforeachでそのまま使う - メニュー未設定時のフォールバックと、記事がない場合の処理を用意する
この構成なら、運用時の並べ替えは管理画面だけで完了します。ヘッダー、トップページ、サイドバーなどが同じ関数を参照するため、カテゴリーの追加や順番変更にも対応しやすくなります。

MEMBER COMMENTS
コメント(0件)