本文へ移動

WP_Query基礎

WordPressで投稿を取得する方法
|get_postsとWP_Queryの使い分け

WordPressで投稿を数件だけ取り出したいとき、get_posts()とWP_Queryのどちらを使うか迷うことがあります。取得後に投稿の配列だけを使うのか、総件数やページ送りまで使うのかを先に決めると選びやすくなります。

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

配列だけなら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_Querythe_post()後のリセット
条件に合う総件数や総ページ数を表示するWP_Queryno_found_rowsをtrueにしない
追加した一覧にページ送りを付けるWP_Querypagedと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()の処理実際の扱い
numberpostsposts_per_pageの別名として使われる。既定5件
categorycatへ変換される
includepost__inへ変換し、取得件数をinclude数へ合わせる
excludepost__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へ、そのまま貼るコードではありません。

PHPtemplate-parts/latest-posts.phpget_postsで同じ6投稿から3件を配列として表示する
$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へ、そのまま貼るコードではありません。

PHPtemplate-parts/latest-posts.php同じ投稿のIDだけを3件取得する
$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へ、そのまま貼るコードではありません。

PHPtemplate-parts/latest-posts.phpWP_Queryで同じ3件とページ情報を扱う
$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_rowstrueへ強制false総件数と総ページ数の計算
ignore_sticky_poststrueへ強制falseホーム相当のクエリでstickyを先頭扱いするか
suppress_filters既定true既定falseposts_where、posts_results、the_postsなどのフィルターを適用するか
posts_per_pagenumberpostsの既定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へ、そのまま貼るコードではありません。

PHPtemplate-parts/latest-posts.phpget_postsでも投稿取得の対象フィルターを適用する
$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へ、そのまま貼るコードではありません。

PHPtemplate-parts/latest-posts.phpget_postsの配列でテンプレートタグを使う
$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
取得したID10、11、1210、11、12
返った件数33
返り値・作ったものWP_Postの配列WP_Queryオブジェクト
no_found_rowstruefalse
ignore_sticky_poststrueへ強制trueを明示
suppress_filters既定true既定false
found_posts返り値になし6
max_num_pages返り値になし2
fields=ids整数を3件WP_Queryでも指定可能

実際の比較画面でも、取得した3件は同じです。違いを見るときは、投稿IDより下にある「条件に合う総件数」と「総ページ数」の行に注目してください。

同じ投稿を取得し、get_postsはWP_Post配列、WP_Queryは総件数6と総ページ数2も持つ実測比較表
ローカルWordPressの実測同じ投稿でも受け取れる情報が違う

同じID 10・11・12を取得しても、受け取れるページ情報は異なります。

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

検証用フィルターで0件にする条件を追加し、get_postsの既定値では3件、抑止解除とWP_Queryでは0件になった実測結果
フィルターの別試験抑止の設定で3件・0件・0件になる

上の返り値比較とは別に、対象クエリだけへ検証用フィルターを加えた結果です。

get_posts()とWP_Queryの違いを確認する公式資料

get_posts()の既定値と返り値、WP_Queryで使える総件数・ページ数・標準ループ、投稿データを準備して戻す方法は、次の公式資料で確認できます。

WordPress公式

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

取得後に使う情報を決めてから選ぶ

取得方法を切り替えるときは、件数だけでなく並び順と、その後に表示するタイトルも見比べます。投稿が同じでもページ情報やフィルターの扱いは異なるため、必要な情報が揃っていることまで確認してから本番へ反映してください。

本番へ反映する前の確認

  • 表示や後続処理に必要な投稿・件数・ページ情報が揃っている
  • 先頭固定投稿とサイト固有フィルターが意図した扱いになっている
  • 投稿データを切り替えた処理の直後でリセットしている
  • パーツを二重表示しておらず、ページ送りを使う場合は2ページ目も確認した
  • ローカルで並び順と処理後の表示を確かめ、変更を戻せる
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月25日 初回公開

この記事を活用する

あとで見返す・共有する

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

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