複数のタクソノミーで絞り込みたいときに使える、「紐づけ」の考え方

※本ブログの目的は個人の備忘録であり、コードは参考用として掲載しています。
実際に使用される際は、ご自身の環境で十分に動作確認を行ってください。
コードの利用によって生じたいかなる問題についても責任を負いかねますので、あらかじめご了承ください。

WordPressで複数のタクソノミーを登録し、絞り込みの機能をつけたいとき、どうすればいいか迷うことはありませんか。

別々のタクソノミーを、どう紐づけすればいいのか。

そんなときは「投稿」を共通点と考え、タクソノミーを紐づけてみましょう。

例:本とジャンル・言語

  • 投稿タイプ:book
  • タクソノミー1:genre(ジャンル)
  • タクソノミー2:language(言語)

目的としては、「ジャンルごとに、どの言語の本があるか」を表示したいといったイメージです。

投稿を紐づけの共通点にする

WordPressではタクソノミー同士を直接紐づけることはできません。

代わりに

  • 投稿(book)が「ジャンル」と「言語」の両方を持つ
  • 両方を持つ投稿を利用して、ジャンルと言語の関係を集める

という風に考えましょう。

それぞれ別のタクソノミーのため、どう紐づけるべきか悩んでしまいますが、「投稿そのもの」を起点に集めることができます。

つまり、投稿を通じてタクソノミー同士を結びつける仕組みです。

配列にまとめるイメージ

例えば投稿が以下のようにあるとします。

投稿

genre

language

book1

SF

英語

book2

SF

日本語

book3

ファンタジー

英語

このとき「ジャンルごとに言語の一覧」を作ると、こんな構造になります。

$genre_languages = [
  'SF' => ['英語', '日本語'],
  'ファンタジー' => ['英語']
];
  • 外側のキー:ジャンル
  • 内側の配列:そのジャンルに存在する言語

この配列を作っておけば、後でセレクトメニューや絞り込みフィルタに簡単に使えます。

PHPで作る場合の考え方

  1. 投稿IDをすべて取得し、1件ずつ処理する
  2. 各投稿に紐づくgenreを取得する
  3. 同じ投稿IDを利用し、languageを取得
  4. genreをキーにしたlanguage配列に追加してまとめる(重複しないようにする)
$query = new WP_Query([
  'post_type'      => 'post',
  'posts_per_page' => -1,
  'fields'         => 'ids', // 投稿IDのみ取得
]);

$post_ids = $query->posts;

$genre_languages = []; // ジャンルごとに「使用されている言語」をまとめるための配列

foreach ($post_ids as $post_id) {

    // 指定した投稿に紐づく「genre」タクソノミーを取得
    // 基本的には1ジャンル想定だが、万一複数設定されている場合も考慮する
    $genres = get_the_terms($post_id, 'genre');

    // genreが取得できた場合のみ、最初のジャンル名を使用
    if (!empty($genres) && !is_wp_error($genres)) {
        $genre = $genres[0]->name;
    } else {
        // ジャンルが取得できない投稿はスキップ
        continue;
    }

    // 同じ投稿に紐づく「language」タクソノミーを取得
    // namesを指定することで、言語名のみの配列を取得する
    $languages = wp_get_post_terms($post_id, 'language', [
        'fields' => 'names'
    ]);

    // まだこのジャンルが配列に存在しなければ、空配列で初期化
    if (!isset($genre_languages[$genre])) {
        $genre_languages[$genre] = [];
    }

    // すでに登録されている言語と、今回取得した言語を結合
    // array_unique で重複する言語名を除外する
    $genre_languages[$genre] = array_unique(
        array_merge($genre_languages[$genre], $languages)
    );
}

関数の補足

  • array_merge:既存の言語と新しい言語をまとめる
  • array_unique:重複を削除

生成される配列 $genre_languagesは以下です。

$genre_languages = [
    'SF' => ['英語', '日本語', 'フランス語'],
    'ファンタジー' => ['英語', '日本語']
];

イメージは「箱に箱を入れる」

  • $genre_languagesはジャンル全体を入れる大きな箱
  • $genre_languages['SF']はSFジャンル専用の箱
  • 配列の中の言語は小箱に整理された情報

こうすると、たとえば「SFジャンルの言語セレクト」や「ジャンル × 言語の絞り込みフィルタ」を簡単に作れます。

タクソノミー絞り込みの応用例

この考え方を応用すると、さまざまな条件でのフィルタリングが可能です。

例1:商品 × ブランド

$brand_products = [];
foreach ($product_ids as $id) {
    $brand = get_the_terms($id, 'brand')[0]->name;
    $product_name = get_the_title($id);

    if (!isset($brand_products[$brand])) {
        $brand_products[$brand] = [];
    }
    $brand_products[$brand][] = $product_name;
}

同じように、他の例でも応用できます。

例2:店舗 × エリア

  • 投稿タイプ:shop
  • タクソノミー:areacategory
  • 「エリアごとにどのカテゴリの店舗があるか」を抽出

例3:イベント × カテゴリ

  • 「カテゴリごとにどの月に開催されるイベントがあるか」を配列化

などなど、使いたい場面はたくさんあると思います。

まとめ

  • WordPressではタクソノミー同士を直接紐づけられない
  • 投稿を共通点として、配列に整理する
  • 外側の配列=親(ジャンル)、内側の配列=子(言語)の構造
  • この構造を作ると、セレクトメニューや絞り込みが簡単に実装できる

複数タクソノミーでの絞り込みは、この「投稿を共通点として配列に整理する」考え方を基本にするとやりやすいです。