【WordPressテーマの作り方 第15回】並び順・抽出条件を自由に!WP_Queryで一覧表示をカスタマイズしよう

「投稿を好きな順番で並べたい!」
「特定のタグやカスタムフィールドの値だけ表示したい…でもうまくいかない」

一覧表示をカスタマイズしたい時、頼りになるのが WP_Query です。
しかし、書き方や使い方を少し間違えると、表示されない・ページネーションが効かないなど、地味にハマるポイントも…。

今回はそんな「自由な一覧表示」をスッキリ実装できるよう、基礎から応用まで丁寧に解説していきます!

目次

✅ 今回のゴール

  • WP_Query で投稿一覧を思い通りに並べ替え・抽出できる
  • カスタム投稿タイプやタクソノミーとの連携ができる
  • get_posts() との違いや使い分けがわかる

🔰 投稿を取得する3つの方法

関数名特徴用途の例
get_posts()配列で取得。簡易的。トップ数件表示など
query_posts()メインループを強制変更※推奨されない
WP_Query柔軟・再利用しやすい複雑な一覧・抽出条件に◎

💡 本シリーズでは 再利用性の高い WP_Query を中心に扱います。

💡 get_posts() のシンプルな使い方

$latest_posts = get_posts([
  'numberposts' => 3,
  'post_type'   => 'post'
]);

foreach ($latest_posts as $post) :
  setup_postdata($post);
  the_title();
endforeach;
wp_reset_postdata();

✅ 配列で取得し foreach でループ
✅ あくまで「簡易用途」に。ページ送りや条件分岐は苦手です

get_posts()WP_Query のラッパー関数として内部的には同じ処理を使っていますが、ページネーションや高度な条件指定には不向きです。
あくまで「数件だけサッと出したいとき」に留めましょう。

🛠 WP_Query の基本形

$args = [
// 投稿タイプが「post」で新着6件を取得する
  'post_type'      => 'post',     // 投稿タイプ(投稿)
  'posts_per_page' => 6,          // 表示件数
  'orderby'        => 'date',     // 並び順(投稿日の新しい順)
  'order'          => 'DESC'      // 降順(新しい順)
];

$query = new WP_Query($args);

if ($query->have_posts()) :
  echo '<ul>';
  while ($query->have_posts()) : $query->the_post();
    echo '<li>' . get_the_title() . '</li>';
  endwhile;
  echo '</ul>';
  wp_reset_postdata(); // ループ後にグローバル変数をリセット
endif;

💡 wp_reset_postdata() を忘れると、メインループに影響します!

📄 ページネーション対応

$paged = get_query_var('paged') ?: 1;

$args = [
  'post_type'      => 'news',
  'posts_per_page' => 6,
  'paged'          => $paged
];

$query = new WP_Query($args);

$paged は明示しないと2ページ目以降が機能しません!

🔍 よく使うパラメータ集

パラメータ説明
'post_type'投稿タイプ(post, news, など)
'category_name'カテゴリースラッグ(news など)
'tag'タグスラッグ
'meta_key'ACF等のカスタムフィールドのキー
'meta_value'フィールド値の絞り込み
'orderby'title, meta_value, date など
'paged'ページネーション時の現在ページ番号

💰 ACFで「価格順に並べる」例

$args = [
  'post_type'      => 'product',
  'posts_per_page' => -1,
  'meta_key'       => 'price',           // フィールド名
  'orderby'        => 'meta_value_num',  // 数値としてソート
  'order'          => 'ASC'
];

💡 meta_value_num によって数値ソートが可能に
💡 ACF側のフィールドタイプを「数値」に設定するのを忘れずに!

🧩 複数条件の絞り込み(AND/OR)

複数の条件を同時に指定したい場合、meta_querytax_query を使います。複数書いた場合は AND 条件で処理され、すべての条件を満たす投稿が抽出されます。

一方、いずれかの条件を満たせばOKという OR 条件にしたいときは、'relation' => 'OR' を指定する必要があります。

🎯 価格順に並べたいとき(meta_value_num)

$args = [
  // 投稿タイプ「voice」を対象にする
  'post_type'   => 'voice',

  // カスタムフィールド「age」年齢30歳以上の投稿を取得(数値比較)
  'meta_query'  => [
    [
      'key'     => 'age',         // フィールド名(例:年齢)
      'value'   => 30,            // 比較値
      'compare' => '>=',          // 以上
      'type'    => 'NUMERIC'      // 数値として比較
    ]
  ],

  // カスタムタクソノミー「voice_tag」で'senior'が付いた投稿を対象にする
  'tax_query'   => [
    [
      'taxonomy' => 'voice_tag',  // タクソノミー名
      'field'    => 'slug',       // スラッグで指定
      'terms'    => 'senior'      // 該当スラッグ
    ]
  ]
];

📅 イベントの未来日だけを表示する例

// 今日以降のイベントを取得(ACFなどで event_date を使っている場合)
$args = [
  'post_type' => 'event',
  'meta_query' => [
    [
      'key'     => 'event_date',
      'value'   => date('Ymd'), // フィールド値とフォーマットを合わせる
      'compare' => '>=',
      'type'    => 'NUMERIC'
    ]
  ]
];
// 💡 ACFの「日付」フィールドを 'Ymd' フォーマットで保存している場合に使える定番パターンです。

🟰 OR条件で複数のカスタムフィールドを比較する例

「年齢が30以上」または「経験年数が5年以上」のどちらかを満たす投稿を取得するには、'relation' => 'OR' を指定して複数条件を並べます。

'meta_query' => [
  'relation' => 'OR', // ← AND ではなく OR に変更。relation を OR にすることで、いずれかの条件を満たす投稿を取得可能
  [
    'key' => 'age',
    'value' => 30,
    'compare' => '>='
  ],
  [
    'key' => 'experience',
    'value' => 5,
    'compare' => '>='
  ]
]

✅ 条件が複数ある場合は meta_querytax_query を併用
✅ OR条件にしたい場合は 'relation' => 'OR' を指定

🛑 query_posts() は使わない!

  • メインクエリを書き換えるため、不具合の温床に
  • ページネーションやテンプレートに予期せぬ影響あり

👉 基本的には WP_Query + wp_reset_postdata() の組み合わせが安心

🧠 よくある落とし穴

ミス対策
投稿が表示されない'posts_per_page' => -1 を指定して全件取得
ページ送りが機能しない'paged' => get_query_var('paged') ?: 1
カスタムフィールドの比較がうまくいかない'type' => 'NUMERIC' を指定

やりたいこと逆引き

🧩 逆引き用チートシート表

やりたいこと書き方例注意点
カスタムフィールドで数値比較'type' => 'NUMERIC'ACF側も「数値」にする
特定カテゴリーで絞る'category_name' => 'news'スラッグ名で指定
未来日のイベントだけ表示'value' => date('Ymd') + 'compare' => '>='フォーマット一致 (Ymd)が必須
OR条件で複数フィールド比較'relation' => 'OR''meta_query'配下に条件を列挙
投稿タイプ別アーカイブ並び替えpre_get_postsorderby 設定

🪄 あえてさらに深掘るなら…

1. pre_get_posts を使ったメインクエリの改造

function custom_order($query) {
  if (!is_admin() && $query->is_main_query() && is_post_type_archive('news')) {
    $query->set('orderby', 'title');
    $query->set('order', 'ASC');
  }
}
add_action('pre_get_posts', 'custom_order');

// 💡 is_admin() を使うことで管理画面に影響しないようにし、
// is_main_query() によってメインクエリのみを対象にしています。
// この2つの条件はセットで使うのが鉄則です!

💡 テンプレートを編集せずに、表示条件をコントロール可能!

2. WP_Query を REST API に応用

add_action('rest_api_init', function () {
  // 独自エンドポイントを追加: /wp-json/custom/v1/products/
  register_rest_route('custom/v1', '/products/', [
    'methods'  => 'GET', // GETメソッドで取得
    'callback' => function () {
      // 商品一覧を取得(全件)
      $query = new WP_Query([
        'post_type'      => 'product',
        'posts_per_page' => -1
      ]);

      $data = [];

      // 各投稿をループして配列に整形
      while ($query->have_posts()) {
        $query->the_post();
        $data[] = [
          'title' => get_the_title(),     // 商品名
          'price' => get_field('price')   // ACFで設定した価格
        ];
      }

      // 投稿データのグローバル変数を元に戻す
      wp_reset_postdata();

      // JSON形式でデータを返す(自動でContent-Type: application/json)
      return wp_send_json($data);
    }
  ]);
});

たとえば、上記コードを使って登録した場合、次のURLでJSONデータが取得できます👇

https://あなたのドメイン/wp-json/custom/v1/products/

wp_send_json() はセキュアなJSON出力に便利

たとえば、上記のような REST API を使った場合、返り値の JSON は以下のようになります:

[
  {
    "title": "商品A",
    "price": "1200"
  },
  {
    "title": "商品B",
    "price": "980"
  }
]

📝 まとめ

  • WP_Query投稿の抽出・並び順を自由に操作 できる
  • get_posts は簡易表示向き、WP_Query は本格的な一覧に最適
  • meta_query, tax_query, paged パラメータの使い方に注意
  • REST APIやpre_get_postsでさらに柔軟な表示制御が可能に!

🔮 次回予告

第16回では、
「投稿の並び順を切り替えるUI」や「フロントでの絞り込み検索」 など、
WP_Queryユーザー操作と連携させる方法 を紹介します!

※ACFで設定した値(例:カテゴリー・価格など)をセレクトボックスやチェックボックスと連動させて、
ユーザーが条件を選ぶことでリアルタイムに一覧を切り替える方法にもチャレンジしていきます!

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

この記事を書いた人

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

目次