本文へ移動

WP_Query基礎

WordPressの一覧を変える?追加する?
|メインクエリとサブクエリの違い

WordPressで記事一覧を調整したい時、先に決めたいのは「今ある一覧を変える」のか「別の一覧を追加する」のかです。前者はメインクエリ、後者は独自のWP_Queryが基本です。似た言葉を図と最小コードで分け、初心者が目的に合う調べ方と実装方法を選べるようにします。

Huddly Huddly 投稿日: 2026年10月17日 読了目安: 8分

今ある一覧を変えるならメイン、別の一覧を足すなら独自WP_Query

カテゴリー・検索・投稿一覧など、WordPressがそのURLのために用意した一覧条件を変えたいなら、メインクエリを対象にします。記事下の関連記事やサイドバーの新着記事など、今のページへ別の一覧を追加したいなら、new WP_Query()などで独自のクエリを作ります。まず「変える」と「追加する」を分けると、メインクエリとサブクエリの違いを実装へ結びつけられます。

最初に決めるのは、今ある一覧を変えるか、別の一覧を追加するか

「メインクエリとは何ですか」と調べても、説明だけでは自分のコードに当てはめにくいものです。先に、画面でやりたいことを1文にします。

やりたいことの例どちらに当たるか
カテゴリー一覧を1ページ6件に変える今ある一覧を変える
検索結果から固定ページを除外する今ある一覧を変える
記事本文の下へ関連記事を3件追加する別の一覧を追加する
サイドバーへ新着記事を5件追加する別の一覧を追加する

最初の2つは、そのURLにもともとある一覧の条件変更です。後の2つは、現在のページに別の一覧を足す処理です。この違いが、実装方法を選ぶ入口になります。

やりたいこと対象基本の選択
カテゴリー・タグ・検索・投稿一覧の条件を変えるWordPressが用意したメインクエリpre_get_postsで対象を絞って変更する
記事下・サイドバー・固定ページ内へ別の一覧を足すテンプレート内の独自クエリnew WP_Query()または用途に応じてget_posts()
ブロックテーマで追加する一覧を作るQuery Loopブロックまずエディターで設定できるか確認する
古いquery_posts()を書き換える置き換え前の役割を確認メイン変更か追加する一覧かで移行先を分ける
アーカイブテンプレート内でnew WP_Query()を作ったからといって、元のメインクエリが変わるわけではありません。取得する一覧がもう1つ増えます。

用語よりページと目的を組み合わせて調べる

まだ方法が決まっていないときは、関数名やメソッド名だけで調べるより、WordPressの「どのようなページ」で「何をしたいか」「何を変えたいか」を組み合わせて検索するところから始めましょう。最初からWP_Queryだけに絞ると、テーマの設定やブロックで済む方法を見落とすこともあります。

やりたいこと最初の検索語用語が分かった後
カテゴリー一覧の表示件数を変えたいWordPress カテゴリー一覧 表示件数 変更WordPress pre_get_posts posts_per_page
検索結果の対象を変えたいWordPress 検索結果 固定ページ 除外pre_get_posts is_search post_type
記事の下に関連記事を足したいWordPress 記事下 関連記事 追加WP_Query 関連記事 category__in
サイドバーに新着を足したいWordPress サイドバー 新着記事 追加WP_Query 新着 posts_per_page

表の左側に近い目的があれば、まずはその言葉で調べます。使えそうな方法が見つかったら、次の順番で自分のサイトにも合うか確かめてください。

調べる順番

  1. 1
    ページを特定する:カテゴリー、検索、投稿詳細、固定ページ、サイドバーなど
  2. 2
    目的を動詞で書く:変える、除外する、追加する、並べるなど
  3. 3
    今ある一覧の変更か、別の一覧の追加かを分ける
  4. 4
    使う方法が見えたら関数名やパラメーター名を検索語へ足す
  5. 5
    公式リファレンスで対象範囲と注意点を確認する
  6. 6
    ローカルまたはステージングで0件・1件・複数件を試す

この順番なら、「メインクエリとサブクエリの違い」という抽象的な検索から、実際のページと目的へ戻れます。逆に、実装例だけを先に貼ると、そのコードが現在の一覧を変えるものか、一覧を追加するものか分からないままになりがちです。

メインクエリは、そのURLに必要な投稿を選ぶ処理

WordPressはURLを受け取ると、どの種類のページかを判断し、そのページに必要な投稿を取得します。カテゴリーURLならそのカテゴリーの投稿、検索URLなら検索条件に合う投稿、個別記事URLなら該当する1件です。開いているURLに必要な投稿を選ぶ、この中心の処理がメインクエリです。

メインクエリは「投稿一覧専用」ではありません。個別記事や固定ページでも、現在表示する投稿を決めるメインクエリがあります。テンプレートは、その結果とページ判定を使って表示を組み立てます。

この記事でいうサブクエリは、追加する一覧のための独自クエリ

記事下の関連記事、サイドバーの新着、固定ページ内の商品一覧など、WordPressがそのページのために用意した投稿とは別の記事を取り出したい時は、独自のクエリを作ります。この「別の一覧を追加するための投稿取得」が、この記事でいうサブクエリです。 WordPress公式ではcustom queryやsecondary queryと表現され、日本語のテーマ制作では「サブクエリ」と呼ばれることがあります。ここでは「追加クエリ」とも呼びます。

なお、SQLについて調べると、別のSELECT文を中に入れる書き方も「サブクエリ」と呼ばれます。ここではその書き方ではなく、WordPressのメインクエリとは別に投稿を取り出す話をしています。

URL・メインクエリ・メインループの流れと、new WP_Query・追加ループ・wp_reset_postdataで投稿データを戻す流れを上下2段の横向き矢印で整理した説明図
変える・追加する今ある一覧と追加する一覧では、使う処理が違う

クエリは投稿を選ぶ条件と取得結果、ループはその結果を1件ずつ表示する処理です。追加ループの後は、global $postが指す記事をメインループ側へ戻します。

クエリとループは同じものではない

混乱しやすいのは、「メインクエリ」と「メインループ」、「追加クエリ」と「追加ループ」が一緒に説明されることです。クエリは、どの投稿をどんな条件で取得するかと、その結果を持つオブジェクトです。ループは、取得結果を1件ずつ進め、タイトルや本文を出力する処理です。

言葉役割代表的なもの
メインクエリ現在のURLに必要な取得条件と結果global $wp_query / is_main_query()
メインループメインクエリの結果を1件ずつ表示have_posts() / the_post()
独自・追加クエリテンプレート内で追加した取得条件と結果$latest_query = new WP_Query($args)
追加ループ独自クエリの結果を1件ずつ表示$latest_query->have_posts() / $latest_query->the_post()

つまり、メインクエリを変えることと、メインループのHTMLを変えることも別です。表示件数や対象投稿を変えるのはクエリ側、カードのタグや見出しを変えるのは主にテンプレート側です。

メインループは、新しいWP_Queryを作らずに結果を表示する

カテゴリーや検索のテンプレートでは、WordPressがすでにメインクエリを実行しています。メインループでは、その結果をそのまま使います。ここからのPHP例はクラシックテーマ向けです。ブロックテーマでは、まずQuery Loopブロックの設定を確認してください。

次のコードは、子テーマのarchive.phpにある既存のメインループ部分と置き換えます。ファイル全体を上書きせず、ヘッダー・フッターや既存のページ送りは残してください。category.phpなど、より優先されるテンプレートがある場合は、実際に読み込まれるファイルのループを対象にします。検索結果ならsearch.phpです。

クラシックテーマのテンプレート(またはテンプレートパーツ)で、一覧を表示したい位置に置く例です。投稿本文やfunctions.phpへ、そのまま貼るコードではありません。

PHParchive.phpWordPressが用意したメインクエリを表示する最小例
<?php if ( have_posts() ) : ?>   <ul class="article-list">      <?php while ( have_posts() ) : ?>         <?php the_post(); ?>         <li>            <a href="<?php the_permalink(); ?>">               <?php the_title(); ?>            </a>         </li>      <?php endwhile; ?>   </ul><?php else : ?>   <p>該当する記事はありません。</p><?php endif; ?>

ここにはnew WP_Query()がありません。カテゴリーや検索など、現在のURLに合う取得条件はWordPressが準備済みだからです。HTMLを調整するだけなら、メインクエリを作り直す必要もありません。

テンプレートの変更は、本番と分けたWordPress環境で表示を確認します。検証用や公開用のサーバーをこれから用意する場合は、XserverのPHP対応やバックアップ条件も確認できます。

追加する一覧では、専用の変数でWP_Queryとループを動かす

サイドバーへ新着記事を追加する場合は、メインとは別の変数を用意します。次の例の目的は、サイドバーへ、公開済みの通常投稿を新しい順に最大3件表示することです。先頭固定の記事を優先せず、ページ送りや総件数の表示も行いません。

子テーマにtemplate-parts/latest-posts.phpを作り、次のコード全体を保存します。続いて、読み込まれているsidebar.phpの新着一覧を出したい位置へ、<?php get_template_part( ‘template-parts/latest-posts’ ); ?>を1回だけ追加してください。パーツの保存と、サイドバーからの呼び出しの両方が必要です。

すでにPHPのコードを書いている場所なら、<?phpと?>は不要です。get_template_part( ‘template-parts/latest-posts’ );だけを書きます。今回のコードは一覧を画面に表示するためのものなので、functions.phpには貼り付けません。

クラシックテーマのテンプレート(またはテンプレートパーツ)で、一覧を表示したい位置に置く例です。投稿本文やfunctions.phpへ、そのまま貼るコードではありません。

PHPtemplate-parts/latest-posts.phpサイドバーへ新着記事を3件追加する基本形
<?php$latest_args = [   'post_type'      => 'post',   'post_status'    => 'publish',   'posts_per_page' => 3,   'orderby'        => 'date',   'order'          => 'DESC',   'ignore_sticky_posts' => true,   'no_found_rows'       => true,]; $latest_query = new WP_Query( $latest_args );?> <?php if ( $latest_query->have_posts() ) : ?>   <aside class="latest-posts" aria-labelledby="latest-posts-title">      <h2 id="latest-posts-title">新着記事</h2>      <ul>         <?php while ( $latest_query->have_posts() ) : ?>            <?php $latest_query->the_post(); ?>            <li>               <a href="<?php the_permalink(); ?>">                  <?php the_title(); ?>               </a>            </li>         <?php endwhile; ?>      </ul>   </aside><?php endif; ?> <?php wp_reset_postdata(); ?>

$latest_queryのhave_posts()とthe_post()を使っているため、どのクエリを進めているかが分かります。0件なら新着一覧の枠を出しません。ignore_sticky_postsは先頭固定の優先を止め、no_found_rowsは今回は使わない総件数の計算を省きます。

追加ループのthe_post()はglobal $postが指す記事を一時的に切り替えるため、終わったらwp_reset_postdata()でメインループ側へ戻します。これは、メインループで投稿を準備した後にサイドバーを呼ぶ配置を想定しています。別の独自ループの内側から呼ぶ場合は、その外側のクエリへ戻す処理を別に考えてください。リセットが必要な理由は、wp_reset_postdata()が必要な理由で詳しく説明しています。

一覧の追加に専用のWP_Queryを使うと決めたら、その取得条件をWP Query Builderで整理できます。メインの一覧を変えるpre_get_postsの代わりではありません。

追加する一覧の条件を組み立てる

投稿タイプ・絞り込み・件数を$argsにし、専用変数のループへ渡します。

メイン一覧の条件変更は、表示前に対象を絞って行う

カテゴリー一覧の件数を変えるために、テンプレート内で同じカテゴリーのnew WP_Query()を作ると、WordPressが実行したメインクエリに加えて、似た投稿取得をもう一度実行することになります。ページネーションやページ判定も別管理になり、必要以上に複雑です。

メインクエリの条件変更には、通常pre_get_postsを使います。ただし、このフックには管理画面や追加クエリも通るため、変更対象を明示します。

次のコードは、子テーマのfunctions.phpのPHP内へ1回だけ追加します。既存の関数やコールバックの内側へ入れず、別の処理として登録してください。archive.phpの表示コードへ貼ると、そのリクエストのメインクエリには間に合いません。変更前のファイルを残し、まず本番と分けた環境で試します。

PHPfunctions.php管理画面を除外し、カテゴリー一覧だけ表示件数を6件へ変える基本形
add_action(   'pre_get_posts',   function ( WP_Query $query ) {      if ( is_admin() || ! $query->is_main_query() ) {         return;      }       if ( $query->is_category() ) {         $query->set( 'posts_per_page', 6 );      }   });

$query->is_main_query()でメインクエリだけに絞り、$query->is_category()でカテゴリー一覧だけに絞っています。フック内では、グローバル関数のis_main_query()ではなく、渡された$queryのメソッドを使います。条件を付けずに変更すると、サイドバーやフッターの追加クエリまで変える可能性があります。

選び方を間違えると起きやすいこと

選択肢の違いと、最初に見る点を次の表で整理します。

状態起きやすい問題見直す方向
アーカイブで同じ一覧をnew WP_Query()し直す同じような投稿取得が増え、ページ送りと条件を別々に管理することになるメインクエリをpre_get_postsで変更できないか確認
追加する一覧のために条件なしでpre_get_postsを使うメインだけでなく他のクエリまで変わる追加する一覧は専用のWP_Queryへ分ける
query_posts()でメインループを書き換える元のメインクエリを置き換え、投稿取得も増える目的を分けてpre_get_postsまたはnew WP_Query()へ移行
追加ループ後にリセットしない後続のタイトルやリンクが追加する一覧の投稿を指すwp_reset_postdata()でglobal $postを戻す
クエリ変数とグローバル関数を混ぜる判定とthe_post()が別の結果を進める同じ独自変数のメソッドに揃える

古いコードのquery_posts()を見つけた場合は、そのままnew WP_Query()へ置き換えないでください。まず、そのコードが「今ある一覧を変える」のか「別の一覧を追加する」のかを確認します。移行方法は、query_postsを使わないほうがいい理由で詳しく説明しています。

新着一覧を追加すると、元の記事はどうなる?

サンプルの記事79に、先ほどの新着一覧を追加してみましょう。メインループでthe_post()を呼び、記事を表示した後に追加しています。一覧の位置が分かりやすいよう、この画面ではサイドバーではなく記事本文の下に配置しました。使う取得・表示・リセットのコードは同じです。WordPress 7.1/PHP 8.4.20で、処理中に参照する記事がどう切り替わるかを比べます。

確認内容結果
記事79のメインクエリのis_main_query()true
new WP_Query()で作った追加クエリのis_main_query()false
追加クエリを作った後のglobal $wp_query元のメインクエリのまま
メインループで準備した投稿ID79
追加ループ中のglobal $post82→81→80の順に切り替わる
wp_reset_postdata()後のglobal $postメインループの投稿ID 79へ復帰
共通サンプルサイトで記事79の本文の下に新着記事82・81・80が並び、リセット後の記事IDは79になっている画面
コードを動かした結果開いている記事に、新着3件を追加する

記事79の本文下にある新着一覧は、掲載コードを実行した出力です。取得IDは82・81・80、リセット後は79です。末尾の確認値は検証用に添えており、メイン判定と追加判定は直前の表で確認できます。

投稿IDはサンプルの値なので、別のサイトでは変わります。新着一覧を追加しても、メインクエリそのものは置き換わりません。 ただし、追加ループ中はglobal $postが新着一覧の記事を指すため、最後にリセットして元の記事へ戻します。

候補を0件・1件・2件に絞った場合も、0件なら枠を出さず、1件・2件ならその数だけ表示されます。いずれもリセット後は記事79へ戻ります。この比較では、上の3件取得の条件に、候補を限定する条件だけを加えています。

pre_get_postsの例も、管理画面相当と公開ページ相当を別のPHPプロセスで試しました。カテゴリーのメインだけが2件指定から6件指定へ変わり、追加クエリ・検索・管理画面相当では2件のままでした。検証データのカテゴリー10には3記事しかないため、6件指定でも実際の表示は3件です。該当記事がないメインループでは「該当する記事はありません。」が出ました。

WP Query Builderは追加する一覧の条件整理に使う

記事下や固定ページ内へ追加する一覧を作る場合は、取得条件をWP Query Builderで組み立てられます。まずPost Type、Post Status、posts_per_pageの最小条件を作り、生成された$argsを独自のWP_Queryへ渡します。

WP Query Builderが作るのは取得条件の$argsです。メインクエリを書き換えるpre_get_postsのフックや、テンプレート内の出力HTMLまでは自動で決まりません。生成コードを置く場所に迷う場合は、WP_Queryはどこに書く?を確認してください。

公開中の画面で、通常投稿・公開済み・3件・日付降順を指定し、自動高速化をオフ、no_found_rowsとignore_sticky_postsをtrueにした未編集の出力が次です。先ほどの新着一覧と、パラメーターの名前・値が一致します。表示ループを含む完成コードは手書きであり、この出力そのものではありません。

PHP公開画面の生成結果新着3件の取得条件だけを生成する
$args = array(    'post_type' => 'post',    'post_status' => 'publish',    'posts_per_page' => 3,    'order' => 'DESC',    'orderby' => 'date',    'no_found_rows' => true,    'ignore_sticky_posts' => true);

この配列だけでは一覧は表示されません。先ほどの完成コードと組み合わせるなら、$latest_argsの配列定義をこの出力へ置き換え、生成部分に限って変数名を$latest_argsへ変更します。元の配列と2つ並べず、その後の専用クエリ・ループ・リセットは残してください。

実装前に6つ答えれば、対象を選びやすい

うまく動かないときは、次の項目を上から見直します。

メイン変更か追加する一覧かの判断

  • 変更したいページのURL種類を言える:カテゴリー、検索、個別記事、固定ページなど
  • そのページにすでにWordPressの一覧があるか確認した
  • 目的を「変える」か「追加する」で言い換えた
  • 取得条件の変更と、カードHTMLなど見た目の変更を分けた
  • メイン変更なら管理画面と追加クエリを除外する条件を考えた
  • 追加する一覧なら専用変数、0件表示、wp_reset_postdata()を含めた

古いquery_posts()を使ったコードが残っている場合は、置き換える理由と移行先も確認しておきましょう。

query_postsを使わないほうがいい理由|WP_Query・pre_get_postsとの違い のサムネイル

is_main_query()と複数ループの公式資料

is_main_query()の判定対象、pre_get_postsで条件を変える時点、複数ループで投稿データを戻す流れは、次の公式資料で確認できます。

WordPress公式

確認に使った公式ドキュメント

メインクエリとサブクエリのよくある質問

実装時によく出る疑問を、短い回答と一緒に確認します。

WP_Queryはすべてサブクエリですか?

いいえ。WordPress本体もメインクエリをWP_Queryオブジェクトとして扱います。テーマ内でnew WP_Query()を使って追加するものは通常、独自クエリまたはsecondary queryです。

メインループとは何ですか?

WordPressが現在のURLのために取得したメインクエリの結果を、have_posts()とthe_post()で1件ずつ表示する処理です。メインクエリという取得と、メインループという表示処理は分けて考えます。

アーカイブテンプレート内でnew WP_Query()を使ってもよいですか?

関連記事など別の一覧を追加する目的なら使えます。元のアーカイブ一覧の件数や対象を変えるために同じ取得を作り直すなら、pre_get_postsでメインクエリを変えられないか先に確認します。

追加ループ後にwp_reset_postdata()が必要なのはなぜですか?

独自クエリのthe_post()がglobal $postの参照先を一時的に変えるためです。その後のthe_title()やthe_permalink()がメインループ側の記事を参照できるように戻します。

pre_get_postsはメインクエリだけに効きますか?

いいえ。フック自体には複数のクエリが通ります。公開ページのメインだけを変えたい場合は、is_admin()で管理画面リクエストを除外し、渡された$query->is_main_query()などで対象を絞ります。

WP Query BuilderBuilderで条件を組み直す条件を選び、WP_Queryのコードを画面で作れます。Builderでクエリを作る

この記事を書いた人

Huddly

Web制作 / フロントエンドエンジニア

WordPressとフロントエンド実装で迷いやすい点を、公式情報と検証結果を分けながら、初学者にも追いやすい順序で整理しています。

この記事の検証情報

検証環境

WordPress
7.1.4
PHP
8.3.36
テーマ
wpq-media + DDEV
対象
カスタム投稿 / taxonomy / meta_query

更新履歴

  • 2026年10月17日 初回公開

この記事を活用する

あとで見返す・共有する

お気に入りに保存できます。役に立ったポイントは、いいねや共有で教えてください。

任意のご支援 役に立ったときだけ、活動を応援できます OFUSEはサイト・記事制作に使います。支援の有無で、読める内容や使える機能は変わりません。