📝

【WordPress】ターム一覧をナビ付きで五十音順表示する際に考えたこと

2024/08/08に公開

この記事について

「日本の駅名を都道府県別に、五十音順で行名のナビゲーションを付けて表示する」という必要があったので、実装のためにコードを書いていました。

上記は北海道の画像ですが、このような感じです。

参考元など幾つかの実装例のページを調べつつ「自分なりに効率の良い書き方をしてみる」という考えをベースにコーディングしてみました。
その際にどんなことを考えて、どんな書き方を採用したかについてをまとめています。
また、都道府県別に出力する内容は省き、シンプルにターム一覧を五十音順にする内容で紹介したいと思います。

参考元

https://hp-maruwakari.com/wp-aiueo/

WordPressの設計

今回はタグで実装しています。
北海道の例でご紹介しますと

このように五十音順で、スラッグには都道府県名と連番を組み込んで登録してある感じです。

五十音順に並べるために駅名の読みが必要になるため、Advanced Custom Fieldsで読み仮名のテキストフィールドを準備しました。


'st-kana'というフィールド名に読み方をカタカナで入れています。
ここはひらがなでもOKですが、どちらかに統一した方が実装が楽になりますね。

基本:ターム一覧を取得するコード

まず基本として、駅名タグ全件を取得する記述は以下のような感じになると思います。

$tags = get_terms( array(
    'taxonomy' => 'post_tag',
    'hide_empty' => false,
    'orderby' => 'st-kana',
    'meta_key' => 'st-kana'
) );

タグであればget_termsの代わりにget_tagsを使ってもOKです。
その場合はtaxonomyパラメータは不要ですね。
※おまけ部分になぜget_termsを採用したかも書いています

hide_emptyをfalseにして、関連投稿がなくても取得させるようにしていますが、その必要がなければこの項目は削除してしまいましょう。
orderbyとmeta_keyで読み仮名の'st-kana'を指定して、あ行から並べるように条件を書いてあげればOKです。

foreachで回してあげれば、tag->nameやtag->slugを使って必要な情報を出力できます。

本題:この実装コードを書く時に考えたこと

さて、本題です。
実装例を調べ、以下のような考えの流れで実装する方法を決めました。

●- 以下、著者の脳内の声 -●
タグ全件をforeach→先頭の読み仮名に応じて行名別配列に入れ直して、その配列を再度回して表示させる
[tags]
↓ foreach
tagの'st-kana'でア~オならあ行配列に入れて…と処理し、か行配列などが生成

生成した行名配列毎にforeachで回して各行名のタームを表示・先頭には「あ行」という行名ラベルを付けるようにする

…うーん、全件取得後にまた配列を幾つも生成するのってスマートじゃないよな…

要はtags全件処理のforeachの際に「あ行」とかの行名ラベルを前に表示させたいタームidだけ分かれば効率的っぽいよな…

下のようなgyo_items変数を準備して、指定タームidで行名出力できると良さそうだな。

$gyo_items = array(
  // idをkey、行名をvalueとする
 1  => 'あ行',
  25 => 'か行',
  39 => 'さ行',
)


PHPのarray_key_existsを使えば、処理中のterm_idとgyo_items変数のkeyのidを見てあげて、trueであれば行名を表示する処理で対応できそう。
https://www.php.net/manual/ja/function.array-key-exists.php

それじゃあgyo_items変数を作るために1回、出力用でもう1回tagsの全件をforeachで回す方法で書いてみるか…
●- 以上 -●

書いたコード

まずは先に紹介したターム全件取得データを引数として、行名ラベル表示処理の変数を作る関数を作りました。

function get_gyo_items( $tags ) {
    // 行名ラベルを付けるidの判定用
    $current_gyo = "";

    // 現在のタームidの行名
    $found_gyo = "";

    // 行名ラベルを出力するidと行名の連想配列
    $gyo_items = [];

    // 行名判定配列
    $jp_gyo = array(
        "あ行" => "アイウエオ",
        "か行" => "カキクケコガギグゲゴ",
        "さ行" => "サシスセソザジズゼゾ",
        "た行" => "タチツテトダヂヅデド",
        "な行" => "ナニヌネノ",
        "は行" => "ハヒフヘホバビブベボパピプペポ",
        "ま行" => "マミムメモ",
        "や行" => "ヤユヨ",
        "ら行" => "ラリルレロ",
        "わ行" => "ワヲン",
    );

    foreach ($tags as $tag) {
        // 現在のタームidの読み仮名の先頭文字取得
        $first_word = get_term_meta($tag->term_id, 'st-kana', true);
        $first_word = mb_substr($first_word, 0, 1);

        // どの行名かを取得
        foreach ($jp_gyo as $key => $value) {
            if ( strpos($value, $first_word) !== false ) {
                $found_gyo = $key;
                break;
            }
        }

        // 取得した行名と判定用行名を比較して、相違なら連想配列に格納
        if ($current_gyo !== $found_gyo) {
            $current_gyo = $found_gyo;
            $gyo_items[$tag->term_id] = $found_gyo;
        }
    }
    return $gyo_items;
}

読み仮名の最初の文字を取得するためにmb_substrを使い、取得した最初の文字が何の行に属するかをjp_gyo変数を使って、found_gyo変数に代入

行名ラベル処理をするidと行名の連想配列gyo_itemsに格納するかを判定させるため、current_gyo変数を準備、found_gyoと文字列比較して違った時だけ、格納させる

という流れです。

次に、実際に出力する関数を書いてみます。

function create_kana_and_tags( $gyo_items, $tags ) {
    // ヒット数→行名ラベル毎のstation-kana-boxのdiv終了タグ生成判定用
    $hit = 0; 

    $html = "";
    $html .= '<nav class="station-kana-nav">';

    // 行名ナビゲーションの出力
    foreach ($gyo_items as $id => $value) {
        $html .= '<a href="#kana-link-'.esc_attr($id).'" class="station-kana-list-link">'.esc_html($value).'</a>';
    }
    $html .= '</nav>';
    $html .= '<div class="station-kana-container">';
    foreach ($tags as $tag) {
        // 行名ラベルを表示するidかチェックする
        if ( array_key_exists($tag->term_id, $gyo_items ) ) {
        
        // ヒット数をインクリメント
        $hit++;
        
        // 行名を連想配列から取得
        $gyo_name = $gyo_items[$tag->term_id];
        
        // 最初の行名以外なら、行名毎のstation-kana-boxクラスのdiv終了タグを入れる
        if ( $hit > 1 ) {
            $html .= '</div>';
        }
        // 行名ラベルを出力
        $html .= '<div id="kana-link-'.esc_attr($tag->term_id).'" class="station-kana-row">'.esc_html($gyo_name).'</div>';
        $html .= '<div class="station-kana-box">';
        }
    // 駅名タグを出力
    $html .= '<a href="'.esc_url(get_tag_link($tag->term_id)).'">'.esc_html($tag->name).'</a>';
	}
    $html .= '</div>';
    echo $html;
}

このようなコードで、冒頭の画像のような行名ナビ付きの駅名タグ一覧を出力できました。

最後に

一応、tagsを一回だけforeachで回すだけでも実装できると思いますが…今回は処理を2つの関数に分けてみました。

実際に書いた内容では
8地方区分タブメニュー

●北海道・沖縄はそのまま紐付くタグ一覧を表示
●北海道・沖縄以外は各都府県のタブメニュー
↓ 
前項でクリック・タップされた都府県に紐付くタグ一覧を表示

こんな感じの実装をしていました。

そういえば、WordPressのコーディング規約では配列は角括弧ではなくてarray()で書くようにという内容になっているんですね…視認性重視ということで、今回の記事でも初期化以外はarray()で記述していました。
https://ja.wordpress.org/team/handbook/coding-standards/wordpress-coding-standards/php/

ターム一覧に行名ナビ・ラベルを作って配置したいというケースの参考になれば幸いです。

おまけ

なぜタグ取得する際にget_tagsではなくget_temrsを採用したか

私の場合、取得するタームのスラッグ文字列検索を行う必要があったため、searchパラメータが利用可能なget_termsを採用しました。
ターム名のみに文字列検索を行えるname__likeパラメータなんかもあるので、状況によって使い分けすると良さそうです。

get_termsの方がカテゴリー・タグ・カスタムタクソノミーのどの場面でも汎用的に使えますね。

参考情報

https://wemo.tech/800

Discussion