【WordPressテーマの作り方 第16回】UIと連動して投稿一覧を絞り込み・並び替えする方法

目次

🔍 「一覧表示をもっと自由にしたい!」そんな時こそフロントUI+WP_Query

「投稿を価格の高い順に並べ替えたい」
「特定のタグやカテゴリだけ絞り込んで表示したい」
──そんなニーズに応えるのが今回のテーマ。

第15回で学んだ WP_Query のパワーを、今度は ユーザー操作 と連携させて、セレクトボックスやチェックボックスからの絞り込みUIを実装します!

✅ 今回のゴール

  • セレクトボックスで並び順を切り替えるUIを作る
  • カテゴリや価格帯など、絞り込み条件を連携させて WP_Query で反映する
  • ページリロード(GETパラメータ)による実装で、初心者でもわかりやすい構成に

🧩 実装構成の全体像

  1. セレクトボックスなどの フォームUI を設置
  2. フォーム送信時に GETパラメータで条件を渡す
  3. WP_Query に渡す $args を、GETパラメータを元に組み立て
  4. 投稿を一覧表示

🧩 実装構成の全体像

📊 データの流れ

[フォームUI]
   ↓ (GET)
[URLに条件が付加される]
   ↓
[PHPでGET値を取得]
   ↓
[WP_Queryの$argsに渡す]
   ↓
[クエリ実行 → 結果を出力]

💡 フォームから渡されたGET値(例:?sort=asc&cat=5)を使って、$args に条件を追加し、WP_Query で絞り込み表示する流れです。

実装後のUIは、以下のようなセレクトボックスが並んだ構成になります。

📋 完成UIイメージ:
----------------------------
 並び順:[ 価格が安い順 ▼ ]
 カテゴリ:[ 野菜 ▼ ]
 価格帯:[ 〜1,000円 ▼ ]
 [ 絞り込む ]
----------------------------

このUIから送信された条件を URL に渡し、それを PHP 側で受け取って表示内容を切り替えていきます。

🧪 並び順セレクトボックスの例(価格昇順 / 降順)

▼ HTML側(並び順UI)

<form method="get" class="p-sortForm">
  <!-- 並び順のセレクトボックスフォーム。GETメソッドで送信し、URLにパラメータを付加する形式 -->
  <label>
    並び順:
    <select name="sort" onchange="this.form.submit()">
      <!-- 初期状態の選択肢 -->
      <option value="">選択してください</option>

      <!-- 価格が安い順を選んだとき。$_GET['sort'] が 'asc' の場合に selected 属性を付与 -->
      <option value="asc" <?php selected($_GET['sort'] ?? '', 'asc'); ?>>価格が安い順</option>

      <!-- 価格が高い順を選んだとき。$_GET['sort'] が 'desc' の場合に selected 属性を付与 -->
      <option value="desc" <?php selected($_GET['sort'] ?? '', 'desc'); ?>>価格が高い順</option>
    </select>
  </label>
</form>

📝補足説明(HTML側に記述しないけど知っておくと◎):

  • selected() は WordPress のテンプレートタグで、選択肢が選ばれている状態を自動で付けてくれる便利関数です。
  • onchange="this.form.submit()" によって、選択した瞬間にフォームが送信され、URLに ?sort=asc などのパラメータが付く仕組みです。

🎨 デザインについて: 本記事のHTMLには .p-sortForm.p-filterForm など、スタイル用のクラス名がついています。 ご自身のサイトのデザインに合わせて、CSSでレイアウトや見た目を調整してくださいね。

🧠 WP_Query 側:GETパラメータで並び順を変える

// 並び順の取得(URLのGETパラメータから取得。未指定時は「asc(価格が安い順)」を初期値に)
$sort_order = $_GET['sort'] ?? 'asc';

// WP_Query 用の引数を設定
$args = [
  'post_type'      => 'product',           // 投稿タイプを「product」に限定(カスタム投稿タイプ)
  'posts_per_page' => 6,                   // 1ページあたり6件表示
  'meta_key'       => 'price',             // 並び替え対象のカスタムフィールド(ACFで定義された価格)
  'orderby'        => 'meta_value_num',    // 数値として並び替え(文字列ではなく数値ソート)
  'order'          => ($sort_order === 'desc') ? 'DESC' : 'ASC', // GETパラメータが desc なら降順、それ以外は昇順
];

// クエリを実行($query に投稿一覧を格納)
$query = new WP_Query($args);

💡補足ポイント

  • 'meta_value_num' を使うことで価格の「数値的な並び替え」が可能になります(”10″と”2″が正しく並ぶ)。
  • $_GET['sort'] ?? 'asc' は「null合体演算子(null coalescing operator)」を使っていて、値が存在しないときの初期値設定に便利です。
  • WP_Query のインスタンスを $query に格納しておくことで、ループ内で柔軟に利用できます。

🔁 WP_Query ループの基本構造をおさらい

ここからは、取得した投稿を実際に一覧として出力していきます。
WordPress では have_posts()the_post() を使って「ループ」を組むのが基本です。

🖥 クエリ結果を出力して一覧表示

<?php if ($query->have_posts()) : ?>
  <!-- 投稿が存在する場合はリスト表示 -->
  <ul class="p-productList">
    <?php while ($query->have_posts()) : $query->the_post(); ?>
      <li>
        <a href="<?php the_permalink(); ?>">
          <!-- 投稿タイトルを表示 -->
          <h4><?php the_title(); ?></h4>

          <!-- ACFのカスタムフィールド「price」の値を表示 -->
          <p>価格:<?php the_field('price'); ?>円</p>
        </a>
      </li>
    <?php endwhile; ?>
  </ul>

  <!-- クエリで書き換えた投稿データをリセット(次のループやテンプレートに影響しないように) -->
  <?php wp_reset_postdata(); ?>
<?php else : ?>
  <!-- 該当投稿がなかった場合のメッセージ -->
  <p>該当する商品は見つかりませんでした。</p>
<?php endif; ?>

💡補足ポイント

  • have_posts()the_post() は WordPress ループの基本関数。
  • the_field('price')ACF プラグインを使用している前提です。
  • wp_reset_postdata()ループ終了後のお作法です。これを忘れると、グローバル変数 $post が壊れて別のテンプレートに影響します。

🔎 複数の絞り込み条件を組み合わせる(カテゴリや価格帯)

▼ UIを拡張

<!-- 絞り込みフォーム(GETメソッドで送信) -->
<form method="get" class="p-filterForm">

  <!-- カテゴリー選択フォーム -->
  <label>カテゴリー:
    <?php
    wp_dropdown_categories([
      'show_option_all' => 'すべて',                // 「すべて」の選択肢を表示(=制限なし)
      'name'            => 'cat',                  // name属性 → GETパラメータ「cat」として送信される
      'selected'        => $_GET['cat'] ?? '',     // 送信された値を保持(再表示時に選択状態を維持)
      'taxonomy'        => 'product_category',     // 使用するタクソノミー(ここでは product_category)
    ]);
    ?>
  </label>

  <!-- 価格帯選択フォーム -->
  <label>価格帯:
    <select name="price_range">
      <option value="">選択してください</option>
      <option value="under1000" <?php selected($_GET['price_range'] ?? '', 'under1000'); ?>>
        〜1,000円
      </option>
      <option value="1000to3000" <?php selected($_GET['price_range'] ?? '', '1000to3000'); ?>>
        1,000〜3,000円
      </option>
      <option value="over3000" <?php selected($_GET['price_range'] ?? '', 'over3000'); ?>>
        3,000円〜
      </option>
    </select>
  </label>

  <!-- フォーム送信ボタン -->
  <button type="submit">絞り込む</button>
</form>

📝 コメントのポイント:

  • wp_dropdown_categories() は WordPressの便利な関数で、指定したタクソノミー(この場合は product_category)の一覧を <select> にしてくれます。
  • $_GET['xxx'] ?? '' で選択値を保持する処理を入れておくと、再読み込み時にも選択状態が保たれて UX が向上します。
  • selected() は WordPress のフォームヘルパー関数で、対象値が選択されたときに selected 属性を出力します。

🧠 条件に応じて $args を追加

🧠 GET値をもとにクエリ条件を構築する

// -----------------------------
// カスタムフィールド(価格)による絞り込み条件を格納
// -----------------------------
$meta_query = [];

// 「価格帯」が選択されている場合は、該当する条件を $meta_query に追加
if (!empty($_GET['price_range'])) {
  switch ($_GET['price_range']) {
    case 'under1000':
      // 1,000円以下の投稿
      $meta_query[] = [
        'key'     => 'price',     // ACFのフィールド名
        'value'   => 1000,        // 上限値
        'compare' => '<=',        // 以下
        'type'    => 'NUMERIC',   // 数値として比較
      ];
      break;

    case '1000to3000':
      // 1,000〜3,000円の範囲
      $meta_query[] = [
        'key'     => 'price',
        'value'   => [1000, 3000], // 範囲指定
        'compare' => 'BETWEEN',    // 範囲内
        'type'    => 'NUMERIC',
      ];
      break;

    case 'over3000':
      // 3,000円以上の投稿
      $meta_query[] = [
        'key'     => 'price',
        'value'   => 3000,
        'compare' => '>=',        // 以上
        'type'    => 'NUMERIC',
      ];
      break;
  }
}

// -----------------------------
// カテゴリー(タクソノミー)による絞り込み条件を格納
// -----------------------------
$tax_query = [];

// 「カテゴリー」が選択されている場合は、$tax_query に条件を追加
if (!empty($_GET['cat'])) {
  $tax_query[] = [
    'taxonomy' => 'product_category', // カスタムタクソノミー名
    'field'    => 'term_id',          // GETで送られてくる値は term_id
    'terms'    => intval($_GET['cat']) // 数値に変換して指定
  ];
}

// -----------------------------
// WP_Query の引数を組み立てる
// -----------------------------
$args = [
  'post_type'      => 'product',      // 投稿タイプ(商品)
  'posts_per_page' => 6,              // 表示件数
  'meta_query'     => $meta_query,    // 価格帯の絞り込み条件
  'tax_query'      => $tax_query,     // カテゴリーの絞り込み条件
];

📝補足アドバイス:

  • meta_querytax_query は、条件がなければ自動的にスルーされるので、安心して動的に追加できます。
  • BETWEEN>= のような比較演算子は、type によって意味が変わるので、数値系なら必ず NUMERIC を明示しましょう。
  • intval() はセキュリティ的にも重要で、GET値を確実に整数に変換することで意図しない値の注入を防ぎます。

📌さらに補足: このサンプルでは、product というカスタム投稿タイプと、product_category というカスタムタクソノミーを使用しています。 まだ未設定の方は、第5回:カスタム投稿タイプを作ろう第13回:ACFで投稿にメタ情報を追加 を参考にしてください。

✅ ページネーション対応($pagedも忘れずに)

// -----------------------------
// 現在のページ番号を取得(ページネーション対応)
// -----------------------------
// get_query_var('paged') は現在のページ番号を取得する関数(1ページ目は0や空になることがある)
// 「 ?: 」は null 合体演算子。値が空なら 1 を代入する(つまり 1ページ目として扱う)
$paged = get_query_var('paged') ?: 1;

// 取得したページ番号を WP_Query の引数に追加
$args['paged'] = $paged;

💡補足ポイント

  • ページネーションを使うときに $paged を明示しないと、2ページ目以降が「表示されない」現象が起きやすいです。
  • 特に WP_Query を手動で書いた場合は、必ず 'paged' => $paged を入れるのが基本です!

📝 まとめ

  • フロントUI(フォーム)と WP_Query を組み合わせることで、柔軟な一覧表示が実現できる
  • $_GET で受け取った値に応じて meta_querytax_query を組み立てる
  • onchange イベントと selected() の工夫でスムーズなUI体験に
  • すべての項目にページネーション処理も忘れずに

💬補足: まだ product 投稿タイプや ACF を導入していない方は、
以下の記事で事前準備の方法を紹介していますので、そちらも参考にしてください👇

🔮 次回予告

第17回では、
ユーザー操作に応じて 非同期(Ajax)で一覧を切り替える 方法にステップアップ!
ページリロードなしで、軽快なフィルターUIを実装していきます!

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

Web制作会社勤務、業界歴10年以上。現場を走り回ってきた制作者。
これからWeb制作を学ぶ人に寄り添いながら、実務で役立つ知識をわかりやすくシェアします。
「ゼロぐらいから学べる!」をモットーに、あなたの“がんばろ”を応援します!

目次