WordPressのカスタムメニュー機能とそのあれこれ

今回は WordPress に関する小ネタです。

このサイトでは 2.7 くらいの頃から WordPress を利用してるのですが、その当時には無い機能だったこともあって、最近色々と WordPress を触る機会があって、今になって「こんな機能あったのかっ?! (いつから?)」みたいなのを結構再発見する機会が多いです。

今回紹介するのはその内のカスタムメニュー機能についてです。ちょっと前に Twitter でも

って触れた話なんですけどね。

カスタムメニューを端的に紹介すると、

  • 管理画面の「外観 > メニュー」から自由な組み合わせでメニュー項目を作れる
    • なので、固定ページとカテゴリを並べてメニュー化することもできます
  • 固定ページやカテゴリなど親子関係を階層という形でメニューに反映できる
    • ……どころか、「外観 > メニュー」から自由な組み合わせを設定できるので、ブログ記事の下階層にトップページを置く、みたいな親子関係を持たない固定ページや投稿、カテゴリなどで多階層のメニューも作れる
    • なので、実際のサイト構成とは異なるメニュー表示も可能で、複雑なサイトだと単純なメニューでは来て欲しいページへなかなか誘導できなくなりがちなケースを、ユーザ動線を意識したメニュー作成が可能
  • カスタムメニューの仕組み上、新しい記事が投稿されれば自動的にメニューに追加されるという機能ではないので、変更する機会が少なめのグローバル・ナビゲーションでの利用に適してる

そんな機能となっています。

カスタムメニュー、あるいはその使い方については WordPress 3.0 から導入された機能で、今から見ると結構古くからある機能とも言えるので、Google などで検索すると色々な紹介ページが既に存在するのですが、

あるいは、

がとても丁寧で分かりやすいです。

カスタムメニューのハマりどころ

前述通りカスタムメニューは今から見ると結構古い機能なので紹介ページは多いのですが、カスタムメニュー機能を紹介する記事が多いものの、実際に触ってみると「えっ?!」ってなる点も結構多いです。カスタムメニュー機能の使い方などは前に紹介したページにおまかせして、今回はその中で自分が実際に経験したハマりどころをちょっとご紹介します。

Twitter を観てくれてる方はお察しかもですが、

この話ですw

カスタムメニューの設置方法を大雑把に紹介すると

  1. register_nav_menu() でカスタムメニューエディタへ登録して、
  2. 管理画面の「外観 > メニュー」で作成したメニューを register_nav_menu() で登録したロケーションと紐付けると
  3. テンプレートに記述した wp_nav_menu() 関数で実際に作成したメニューが表示できる

って流れになりますが、この wp_nav_menu() 関数が結構な曲者で、テンプレートタグ/wp nav menu – WordPress Codex 日本語版での紹介で、使い方に

<?php wp_nav_menu( $args ); ?>

とあり、その後に引数となるパラメータの紹介があるのですが、

$args
配列) (オプション) パラメータを要素とする配列。

初期値: 空の配列

配列には次のパラメーターを入れることができます。パラメーターの名前と値を配列のキー名と値にします。

使い方の例示にある引数、オプションなんです。

引数がオプションなので未定義だとデフォルトの設定が割り当てられ、そのデフォルト値もテンプレートタグ/wp nav menu – WordPress Codex 日本語版に紹介があるのですが、theme_location の初期値は空です。

すると「なるほど、wp_nav_menu($args)$argstheme_location が無ければ実質無視されるってことだよね」って思いがちですが、実際には何が起こるかというと、前に紹介した大雑把な設置方法の 2. の工程を省いてもメニューが表示されちゃうんです。しかもこれ、

theme_location パラメーターが与えられなかった場合、このタグは次の規則に従います。

  • menu パラメーターが与えられている場合、それ(ID、スラッグ、または名前)に一致するメニュー;
  • さもなくば、最初の空でないメニュー;
  • さもなくば(ひとつもメニューが定義されていない場合)、fallback_cb パラメーターで与えられた関数を呼び出す(デフォルトでは wp_page_menu());
  • さもなくば(fallback_cb が関数ではない)、何も表示しない。

注: WordPress 3.5 以降、メニュー項目がない場合は、HTML マークアップはなにも出力されなくなりました。

とある通り、仕様なんですw

即ち、テンプレートの何処かに wp_nav_menu() があると、管理画面で作成したカスタムメニューがそこで表示され、実質作り置きしておけない、ってことなんです(管理画面で作成したカスタムメニューが2つ以上存在する場合、1つめが呼び出されるので2つめ以降のメニューは作り置きしておくことはできますが……)。

なので、前の 2. の工程で紐付けたカスタムメニュー以外は表示してほしくないって時は

<?php if ( has_nav_menu('HeaderGlobalNavi') ): ?>
<nav id="header-column">
    <?php wp_nav_menu( array(
        'theme_location'  => 'HeaderGlobalNavi',
        'container'       => 'div',
        'container_id'    => 'header-navi',
        'container_class' => 'collapse navbar-collapse',
        'menu_class'      => 'nav navbar-nav',
        'items_wrap'      => '<ul class="%2$s">%3$s</ul>',
    )); ?>
</nav>
<?php endif; ?>

のように has_nav_menu()theme_location と紐付けられた register_nav_menu() が存在するかを判定しておく必要があるんですよね。ここ、結構なハマりどころです。

更に付け加えると、

例えば上の has_nav_menu() で判定を行なってから wp_nav_menu() を実行するサンプルなのですが、has_nav_menu() の判定がない

<nav id="header-column">
    <?php wp_nav_menu( array(
        'theme_location'  => 'HeaderGlobalNavi',
        'container'       => 'div',
        'container_id'    => 'header-navi',
        'container_class' => 'collapse navbar-collapse',
        'menu_class'      => 'nav navbar-nav',
        'items_wrap'      => '<ul class="%2$s">%3$s</ul>',
    )); ?>
</nav>

だと、前述の通り最初の空でないメニューが無条件で呼ばれるだけでなく、container などのカスタムメニューを表示するHTML要素や属性設定がまるっとスルーされて、単純に意図しないメニューが呼ばれるだけでなく、意図しないHTMLでメニューが表示されるのでビジュアルまで崩れてくれますw

まとめ

カスタムメニューは既にいろいろなサイトやページで紹介されてる機能なので、今回の記事ではハマりどころを紹介しましたが、変更する機会が少なめのグローバル・ナビゲーションでは特に有用で、自由な構成でメニューが作成できる特性からも、ページ数が多いサイトでのユーザ動線改善にも使える、かなり便利で奥が深い機能なのは間違いありません。

WordPress に限った話ではありませんが、長く使ってるその間も継続的に開発・バージョンアップが行われているプロダクトは、その間に便利な新機能が追加されても、ついつい古いやり方を踏襲しがちです。

ですが普段使っているプロダクトなら、なおさらプロダクトの新機能に対して情報感度を上げて、便利な機能はどんどん取り入れられるようにしたいものですね。