配列だけならget_posts、クエリ情報まで使うならWP_Query
最新記事や特定IDを数件だけ取得し、WP_PostまたはIDの配列として使うならget_posts()が分かりやすい選択です。独自ループ、総件数、総ページ数、ページ送り、クエリの状態まで扱うならWP_Queryを使います。
迷ったら、「投稿のタイトルやリンクだけで足りるか」「何ページあるかも知りたいか」から考えてみましょう。
最初に返り値とページ情報を比べる
get_posts()とWP_Queryの違いは、コードの行数より「取得後に何を使えるか」に表れます。ここでいう返り値は、関数や処理を呼んだあとに受け取る値のことです。get_posts()からは通常、投稿を表すWP_Postの配列を受け取ります。new WP_Query( $args )では、取得結果やループの状態を持つWP_Queryオブジェクトが作られます。
たとえば3件のタイトルだけなら、配列の中から1件ずつ取り出して使えます。「全6件のうち3件を表示していて、全体は2ページ」という情報も使いたいなら、WP_Queryを選びます。
| 比較項目 | get_posts() | WP_Query |
|---|---|---|
| 返り値 | WP_Postまたは整数の配列 | WP_Queryオブジェクト |
| 投稿結果 | 返り値の配列を直接使う | $query->postsとループメソッドを使える |
| 今回返った件数 | count($posts)で数える | $query->post_count |
| 条件に合う総件数 | 返り値からは取得できない | $query->found_posts |
| 総ページ数 | 返り値からは取得できない | $query->max_num_pages |
| 標準ループ | 配列のforeach、またはsetup_postdata() | have_posts()とthe_post() |
| 主な用途 | 数件の表示、ほかの処理へ渡す配列 | 追加した一覧、ページ送り、テンプレートタグ |
WP_Postは、1件の投稿のID・タイトル・本文などを持つオブジェクトです。WP_Queryはその投稿を並べた配列だけでなく、取得条件、件数、総ページ数、ループがどこまで進んだかも持ちます。投稿そのものと、投稿を取得して扱うための情報を分けると理解しやすくなります。
ページネーションを伴わず、数件の投稿だけを使う処理にはget_posts()が向いています。やりたいことが決まっている方は、次の表から選んでみてください。
やりたいことに合わせて取得方法を選ぶ
投稿を取得したあと、何を表示したり処理したりしたいでしょうか。必要な情報に合わせると、候補を絞れます。
| やりたいこと | 選ぶ方法 | 確認すること |
|---|---|---|
| 数件のタイトルやリンクを表示する | get_posts() | 配列で足りるか、ページ送りは不要か |
| 投稿IDをほかの処理へ渡す | get_posts()+fields=ids | 整数の配列を使う処理か |
| ループでタイトル・抜粋・画像を表示する | WP_Query | the_post()後のリセット |
| 条件に合う総件数や総ページ数を表示する | WP_Query | no_found_rowsをtrueにしない |
| 追加した一覧にページ送りを付ける | WP_Query | pagedとmax_num_pagesを使う |
| サイトの投稿取得フィルターを適用する | WP_Query、または設定を変えたget_posts() | suppress_filtersと対象フックの動作 |
数件だけでも、テンプレートタグを組み合わせた表示ならWP_Queryを選んで構いません。短く書ける方へ無理に揃えず、その後の処理が分かりやすくなる方を使いましょう。
get_postsはWP_Queryを内部で呼ぶラッパー関数
get_posts()も、投稿を探す処理そのものは内部のWP_Queryに任せています。受け取った取得条件を整えてWP_Queryへ渡し、取得された投稿の配列を返します。このように、別の処理を呼び出しやすく包んだ関数を「ラッパー関数」と呼びます。
そのため、投稿タイプのpost_type、件数のposts_per_page、絞り込みのtax_queryやmeta_query、並び順のorderbyなど、多くのパラメーターを共通して使えます。
ただし、共通の引数を受け取れることと、同じ結果・同じ情報を返すことは別です。get_posts()は用途に合わせて一部の引数を補い、クエリオブジェクトを外へ返しません。
| get_posts()の処理 | 実際の扱い |
|---|---|
| numberposts | posts_per_pageの別名として使われる。既定5件 |
| category | catへ変換される |
| include | post__inへ変換し、取得件数をinclude数へ合わせる |
| exclude | post__not_inへ変換される |
| ignore_sticky_posts | 渡した値にかかわらずtrue |
| no_found_rows | 渡した値にかかわらずtrue |
| suppress_filters | 既定true。明示的にfalseへ変更できる |
get_postsはWP_Postの配列を直接使う
次の例は、検証用の公開投稿6件(ID 10〜15)からIDの小さい順に3件を取得します。この先のコード・表・画像も同じ投稿を使います。ご自身の環境ではIDを置き換え、全投稿を対象にする場合はpost__inを外してください。
子テーマのtemplate-parts/latest-posts.phpへ保存し、たとえばsingle.phpのメインループでthe_post()を呼んだ後にget_template_part( ‘template-parts/latest-posts’ );を1回記述すると、その場所へ一覧を追加できます。クラシックテーマで、パーツの保存と呼び出しを組み合わせる例です。
新規のPHPファイルなら先頭に<?phpを置き、既存のPHP内へ追加する場合は開始タグを重ねません。呼び出す側の1行もPHPの内側へ記述します。各WP_Postをget_permalink()とget_the_title()へ直接渡すため、テンプレートタグが参照する投稿データは切り替わりません。
クラシックテーマのテンプレート(またはテンプレートパーツ)で、一覧を表示したい位置に置く例です。投稿本文やfunctions.phpへ、そのまま貼るコードではありません。
$latest_posts = get_posts( [ 'post_type' => 'post', 'post_status' => 'publish', 'posts_per_page' => 3, 'post__in' => [ 10, 11, 12, 13, 14, 15 ], 'orderby' => 'ID', 'order' => 'ASC', ]); if ( $latest_posts ) { echo '<ul>'; foreach ( $latest_posts as $latest_post ) { printf( '<li><a href="%1$s">%2$s</a></li>', esc_url( get_permalink( $latest_post ) ), esc_html( get_the_title( $latest_post ) ) ); } echo '</ul>';}foreachで配列から1件ずつ取り出し、その投稿のリンクとタイトルを表示しています。この書き方ではsetup_postdata()を使っていないため、wp_reset_postdata()も不要です。
取得方法を切り替えるときは、本番と分けたWordPress環境で、投稿の並びと処理後の表示を見ておくと安心です。検証や公開に使うサーバーをこれから用意する場合は、XserverのPHP対応やバックアップ条件も確認できます。
投稿IDだけを使うならfieldsをidsにする
投稿IDだけが必要なら’fields’ => ‘ids’を追加します。最初のパーツの取得・表示処理を次のコードへ置き換えると、同じ3件のIDが$latest_idsへ入ります。この断片だけでは画面に表示せず、IDを必要とする次の処理へ渡して使います。
クラシックテーマのテンプレート(またはテンプレートパーツ)で、一覧を表示したい位置に置く例です。投稿本文やfunctions.phpへ、そのまま貼るコードではありません。
$latest_ids = get_posts( [ 'post_type' => 'post', 'post_status' => 'publish', 'posts_per_page' => 3, 'fields' => 'ids', 'post__in' => [ 10, 11, 12, 13, 14, 15 ], 'orderby' => 'ID', 'order' => 'ASC', ]);WP_Queryは専用オブジェクトと標準ループを使う
同じ6投稿・3件・ID昇順の条件でWP_Queryを作ると、取得した投稿に加えて件数やページ情報も使えます。次は最初のパーツを丸ごと置き換える別の実装です。両方を並べると一覧が2回出るため、比較後は使う方だけ残します。
$latest_query->have_posts()で次の投稿があるかを確かめ、$latest_query->the_post()でその投稿へ進みます。どちらも$latest_query->から始めることで、今回取得した同じクエリの結果を使っています。
クラシックテーマのテンプレート(またはテンプレートパーツ)で、一覧を表示したい位置に置く例です。投稿本文やfunctions.phpへ、そのまま貼るコードではありません。
$latest_query = new WP_Query( [ 'ignore_sticky_posts' => true, 'post_type' => 'post', 'post_status' => 'publish', 'posts_per_page' => 3, 'post__in' => [ 10, 11, 12, 13, 14, 15 ], 'orderby' => 'ID', 'order' => 'ASC', ]); if ( $latest_query->have_posts() ) { echo '<ul>'; while ( $latest_query->have_posts() ) { $latest_query->the_post(); printf( '<li><a href="%1$s">%2$s</a></li>', esc_url( get_permalink() ), esc_html( get_the_title() ) ); } echo '</ul>';} wp_reset_postdata();the_post()を使うと、テンプレートタグが参照する投稿データが一時的に変わります。ここではメインループから呼び出しているため、終了後のwp_reset_postdata()でメインループの投稿へ戻せます。追加した別のループの中へさらに入れ子にする場合は、戻す対象に注意が必要です。リセットの使い方も参考にしてください。
ignore_sticky_posts=trueは、get_posts()と同じく先頭固定投稿の優先を無効にする指定です。取得条件を揃えるために追加しています。このコードでは$latest_query->found_postsが6、$latest_query->max_num_pagesが2になりますが、ページリンクはまだ出していません。現在ページを取得してpagedへ渡し、リンクを出力するところまでの手順はページネーションの実装例で説明しています。
WP_Queryで一覧を作るなら、投稿タイプ・件数・並び順をWP Query Builderで組み立ててみましょう。生成された$argsを土台に、今回の表示コードと合わせて試せます。
投稿の取得条件を組み立てる
条件を画面で選び、生成コードと説明を見比べられます。総件数も使う場合はno_found_rowsの設定を確かめ、WordPress上の結果と合わせて確認します。
既定値はno_found_rows・sticky・filtersで異なる
同じ$argsを渡しても、省略した設定に補われる値や、get_posts()側の上書きで動作が変わります。表の「既定」は、自分で値を指定しなかった場合の設定です。
no_found_rowsは総件数の計算を省くか、ignore_sticky_postsは先頭固定投稿を優先する特別扱いを無視するかの設定です。suppress_filtersは投稿取得に関わる一部のフィルターを抑止する設定で、trueなら抑止、falseなら適用します。いずれも「trueなら機能を有効にする」と一括りにせず、何を省く設定なのかを見ると迷いにくくなります。
| 設定項目 | get_posts() | new WP_Query()の基本既定 | 影響 |
|---|---|---|---|
| no_found_rows | trueへ強制 | false | 総件数と総ページ数の計算 |
| ignore_sticky_posts | trueへ強制 | false | ホーム相当のクエリでstickyを先頭扱いするか |
| suppress_filters | 既定true | 既定false | posts_where、posts_results、the_postsなどのフィルターを適用するか |
| posts_per_page | numberpostsの既定5件を使用 | サイト設定の表示件数 | 引数を省略した時の取得件数 |
ignore_sticky_posts=trueは、sticky投稿を結果から除外する指定ではありません。先頭へ移動する特別扱いを無視し、通常の並びに残します。完全に除外したい場合は、対象IDを確認した上でpost__not_inなど別の条件が必要です。
フィルターに依存するサイトではsuppress_filtersを確認する
フィルターは、WordPressの処理途中の値を、プラグインや自分のコードで変更する仕組みです。たとえばposts_whereはSQLの絞り込み条件、the_postsは取得後の投稿配列へ処理を加えます。suppress_filters=trueでは、こうした対象フィルターを通さずに取得します。
suppress_filters=trueは、プラグインの影響をすべてなくす設定ではありません。 アクションフックのpre_get_postsなどは、この設定でも動きます。
その結果、同じ$argsでもget_posts()とWP_Queryで投稿数や順番が異なる可能性があります。フィルターを適用する必要が明確な時だけ、次のようにfalseを指定します。
最初のパーツの取得部分を次へ置き換え、$filtered_postsを表示処理へ渡して使います。フィルターを新しく登録するコードではなく、すでに登録された対象フィルターを適用する指定です。
クラシックテーマのテンプレート(またはテンプレートパーツ)で、一覧を表示したい位置に置く例です。投稿本文やfunctions.phpへ、そのまま貼るコードではありません。
$filtered_posts = get_posts( [ 'post_type' => 'post', 'post_status' => 'publish', 'posts_per_page' => 3, 'post__in' => [ 10, 11, 12, 13, 14, 15 ], 'orderby' => 'ID', 'order' => 'ASC', 'suppress_filters' => false, ]);切り替える前に、サイトでどのフィルターを使っているかを調べておくと安心です。対象のフックと処理が分かったら、ローカル環境でtrueとfalseの結果を比べてみましょう。
get_postsでもsetup_postdataを使えばリセットが必要
get_posts()だから常にリセット不要、というわけではありません。返ったWP_Postを直接参照するだけなら不要です。setup_postdata()でテンプレートタグ用の投稿データを準備した場合は、処理後にwp_reset_postdata()を呼びます。
次は最初のパーツを置き換え、同じ3投稿のタイトルをテンプレートタグで表示する例です。single.phpのメインループでthe_post()を呼んだ後から使う前提は同じです。
クラシックテーマのテンプレート(またはテンプレートパーツ)で、一覧を表示したい位置に置く例です。投稿本文やfunctions.phpへ、そのまま貼るコードではありません。
$latest_posts = get_posts( [ 'post_type' => 'post', 'post_status' => 'publish', 'posts_per_page' => 3, 'post__in' => [ 10, 11, 12, 13, 14, 15 ], 'orderby' => 'ID', 'order' => 'ASC', ]); if ( $latest_posts ) { global $post; foreach ( $latest_posts as $post ) { setup_postdata( $post ); the_title( '<p>', '</p>' ); } wp_reset_postdata();}setup_postdata()はグローバルの$post自体を代入しません。この例ではglobal $postとforeachの代入を組み合わせて、各テンプレートタグが参照する投稿も揃えています。直接投稿を渡せる処理なら、最初の例のようにグローバルを切り替えない書き方の方が単純です。
WP_Queryの取得条件をWP Query Builderで整理する
独自ループやページ情報が必要でWP_Queryを選んだ場合は、WP Query Builderで投稿タイプ、絞り込み、並び順、表示件数を整理できます。Builderが生成する$argsと、記事のコードへ置いた$argsを見比べます。
投稿タイプ・3件・対象ID 10〜15を入力すると、今回の条件の基本形を作れます。現在の並び順の選択肢にはIDがないため、画面ではpost__inを選び、入力したIDの順に取得する形で確認できます。この記事と同じID昇順のコードにする場合は、生成後にorderbyをIDへ手動で変更します。
生成コードにはキャッシュや総件数などの設定も含まれます。ページ情報が必要ならno_found_rows=false、投稿取得フィルターを使うならsuppress_filters=falseかも確かめましょう。本文の手書きコードと生成コードを、未確認のまま同じものとは扱わないのがポイントです。
WordPress上の設置場所、表示HTML、リセットは、この記事の例と合わせて実装します。操作手順はWP Query Builderの使い方で説明しています。
同じ条件で取得結果がどう変わるか確かめる
WordPress 7.1/PHP 8.4.20の分離したローカル環境で、本文と同じ公開投稿ID 10〜15から、ID昇順に3件を取得して比べます。ID 13が先頭固定されているため、WP_Queryにもignore_sticky_posts=trueを指定して条件を揃えています。最初の比較では検証用の投稿取得フィルターを追加していません。
| 確認項目 | get_posts() | WP_Query |
|---|---|---|
| 取得したID | 10、11、12 | 10、11、12 |
| 返った件数 | 3 | 3 |
| 返り値・作ったもの | WP_Postの配列 | WP_Queryオブジェクト |
| no_found_rows | true | false |
| ignore_sticky_posts | trueへ強制 | trueを明示 |
| suppress_filters | 既定true | 既定false |
| found_posts | 返り値になし | 6 |
| max_num_pages | 返り値になし | 2 |
| fields=ids | 整数を3件 | WP_Queryでも指定可能 |
実際の比較画面でも、取得した3件は同じです。違いを見るときは、投稿IDより下にある「条件に合う総件数」と「総ページ数」の行に注目してください。

同じID 10・11・12を取得しても、受け取れるページ情報は異なります。
次は、結果を0件にする比較用のposts_whereフィルターがある場合です。画像の上から順に、get_posts()の既定では3件、suppress_filters=falseへ変えると0件、WP_Queryの既定でも0件です。どれも先頭固定の優先は無効のままで、0件なのは取得失敗ではなく、フィルターが効いた結果です。サイト固有のフィルターがある場合も、設定を1つずつ比べると違いを追いやすくなります。

上の返り値比較とは別に、対象クエリだけへ検証用フィルターを加えた結果です。
get_posts()とWP_Queryの違いを確認する公式資料
get_posts()の既定値と返り値、WP_Queryで使える総件数・ページ数・標準ループ、投稿データを準備して戻す方法は、次の公式資料で確認できます。
WordPress公式
確認に使った公式ドキュメント
取得後に使う情報を決めてから選ぶ
取得方法を切り替えるときは、件数だけでなく並び順と、その後に表示するタイトルも見比べます。投稿が同じでもページ情報やフィルターの扱いは異なるため、必要な情報が揃っていることまで確認してから本番へ反映してください。
本番へ反映する前の確認
- 表示や後続処理に必要な投稿・件数・ページ情報が揃っている
- 先頭固定投稿とサイト固有フィルターが意図した扱いになっている
- 投稿データを切り替えた処理の直後でリセットしている
- パーツを二重表示しておらず、ページ送りを使う場合は2ページ目も確認した
- ローカルで並び順と処理後の表示を確かめ、変更を戻せる
この記事の検証情報
検証環境
- WordPress
- 7.1.4
- PHP
- 8.3.36
- テーマ
- wpq-media + DDEV
- 対象
- カスタム投稿 / taxonomy / meta_query
更新履歴
- 2026年10月25日 初回公開
この記事を活用する
あとで見返す・共有する
お気に入りに保存できます。役に立ったポイントは、いいねや共有で教えてください。


