2026年5月28日
2026年5月28日
WordPressのget_postsとWP_Queryの違い|既定値・ページ送り・使い分けの基準
get_posts() は内部で WP_Query を使って投稿を取得し、結果を投稿オブジェクトの配列として返す関数です。つまり両者の違いは取得の仕組みではなく、既定値と戻り値の形にあります。この記事では、その違いがどこで表示や動作の差になって現れるかを整理し、「ページ送りが要るならWP_Query」「配列が欲しいだけならget_posts」という使い分けの基準を、つまずきやすい点と一緒に解説します。
結論:使い分けの早見表
細かい説明の前に、判断の基準をまとめておきます。
| 場面 | 向いている方法 |
|---|---|
| サイドバーの「最新記事5件」など、件数固定の一覧 | get_posts() |
| ページ送り(2ページ目、3ページ目…)がある一覧 | WP_Query |
| 総件数(「全○件」)を表示したい | WP_Query |
| IDだけ取得して別の処理に使いたい | どちらでも可(fields => 'ids') |
| アーカイブや検索結果など、メインの一覧を変えたい | どちらも使わず pre_get_posts |
最後の行が重要で、トップページやカテゴリーアーカイブに表示される投稿を変えたいときに、テンプレートの中で新しいクエリを作り直すのは遠回りです。これについては後半で説明します。
get_posts() の正体
get_posts() は引数を受け取り、いくつかの既定値を補ったうえで new WP_Query() を実行し、その posts プロパティ(投稿の配列)だけを返します。
<?php
$recent = get_posts( array(
'post_type' => 'post',
'posts_per_page' => 5,
) );
foreach ( $recent as $p ) {
printf(
'<li><a href="%s">%s</a></li>',
esc_url( get_permalink( $p ) ),
esc_html( get_the_title( $p ) )
);
}
戻り値はただの配列なので、foreach で回し、get_permalink( $p ) や get_the_title( $p ) のように投稿を引数で渡して使います。the_title() のような「現在の投稿」を前提にしたテンプレートタグを使いたい場合は、setup_postdata() で現在の投稿を設定し、終わったら wp_reset_postdata() で元に戻します。
<?php
global $post;
$recent = get_posts( array( 'posts_per_page' => 5 ) );
foreach ( $recent as $post ) {
setup_postdata( $post );
?>
<li><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></li>
<?php
}
wp_reset_postdata();
setup_postdata() は渡した投稿をもとにテンプレートタグ用のグローバル変数を設定しますが、the_permalink() などはグローバルの $post を参照するため、ループ変数を $post にしてグローバル宣言しておく必要があります。ここを別の変数名にすると、すべての行に同じタイトルが表示されることがあります。
既定値の違いが生む4つの差
get_posts() はWP_Queryに渡す前に既定値を上書きしています。代表的なものは次の4つで、これが「同じ引数なのに結果が違う」原因になります。
1. 取得件数の既定値
get_posts() の既定の件数は5件です(numberposts という引数名でも指定でき、posts_per_page と同じ意味です)。一方、WP_Query で件数を省略すると、管理画面の「表示設定」にある1ページあたりの表示件数が使われます。どちらも件数を明示しておくと、設定変更の影響を受けません。
2. suppress_filters
get_posts() は既定で suppress_filters => true になっており、posts_where や posts_join などのSQLを書き換えるフィルターが適用されません。多言語化プラグインなど、これらのフィルターで言語ごとの絞り込みを行うプラグインを使っている場合、get_posts() では言語の絞り込みが効かないことがあります。その場合は 'suppress_filters' => false を指定します。
なお、pre_get_posts アクションは suppress_filters の値に関係なく実行されます。pre_get_posts で条件を書き換えている場合は、get_posts() の結果にも影響する点に注意してください。
3. 固定表示(先頭に固定)の投稿
get_posts() は既定で ignore_sticky_posts => true です。WP_Query では、条件によっては「先頭に固定表示」した投稿が一覧の先頭に追加され、指定した件数より多く表示されることがあります。件数をぴったりそろえたい一覧では、WP_Query でも ignore_sticky_posts => true を指定します。
4. 総件数の計算(no_found_rows)
get_posts() は既定で no_found_rows => true になっていて、総件数を数える処理を省いています。そのため get_posts() では総ページ数がわからず、ページ送りは作れません。逆に WP_Query は既定で総件数を計算するため、found_posts(総件数)と max_num_pages(総ページ数)が使えます。ページ送りが不要な一覧で WP_Query を使う場合は、'no_found_rows' => true を付けると無駄な計算を減らせます。
WP_Queryでページ送り付きの一覧を作る
ページ送りが必要な一覧は WP_Query で作ります。固定ページのテンプレートなどでサブクエリを作る場合、現在のページ番号は get_query_var( 'paged' ) で取得します。
<?php
$paged = max( 1, (int) get_query_var( 'paged' ) );
$news = new WP_Query( array(
'post_type' => 'news',
'posts_per_page' => 10,
'paged' => $paged,
) );
if ( $news->have_posts() ) :
echo '<ul>';
while ( $news->have_posts() ) :
$news->the_post();
printf( '<li><a href="%s">%s</a></li>', esc_url( get_permalink() ), esc_html( get_the_title() ) );
endwhile;
echo '</ul>';
echo paginate_links( array(
'total' => $news->max_num_pages,
'current' => $paged,
) );
endif;
wp_reset_postdata();
$news->the_post() は現在の投稿を切り替えるため、ループの後で wp_reset_postdata() を呼ばないと、その後に出力されるサイドバーやコメント欄が別の投稿のものになってしまいます。また、静的なフロントページに設定した固定ページでは、ページ番号が paged ではなく page に入ることがあるため、フロントページで2ページ目が表示されない場合はそちらも確認してください。
メインの一覧を変えるなら pre_get_posts
「カテゴリーアーカイブの表示件数だけ変えたい」「トップの一覧から特定カテゴリーを除外したい」といった場合、テンプレートで WP_Query を作り直すと、WordPressが最初に実行したメインクエリが無駄になり、ページ送りの総ページ数もずれます。メインクエリそのものを書き換えるには、pre_get_posts アクションを使います。
<?php
add_action( 'pre_get_posts', function ( $query ) {
if ( is_admin() || ! $query->is_main_query() ) {
return;
}
if ( $query->is_category() ) {
$query->set( 'posts_per_page', 20 );
}
} );
is_admin() と is_main_query() の判定を省くと、管理画面の投稿一覧やウィジェットのクエリまで書き換わります。なお query_posts() はメインクエリを上書きして不具合の原因になりやすいため、公式のドキュメントでも使わないよう案内されています。
パフォーマンス面で押さえておくこと
どちらを使っても、実行されるSQLは引数次第です。大規模なサイトでは次の点を意識します。
'posts_per_page' => -1(全件取得)は避け、必要な件数に絞る- IDだけが必要なら
'fields' => 'ids'を指定し、投稿オブジェクトを組み立てる処理を省く - ページ送りが不要なら
'no_found_rows' => true - メタ情報やタームを使わないなら
'update_post_meta_cache' => false、'update_post_term_cache' => false meta_queryで値を比較する絞り込みは、投稿数が多いと遅くなりやすい。タクソノミーで代用できないか検討する
実際に発行されたSQLと実行時間は、Query Monitor プラグインで確認できます。作業はステージング環境で行い、変更前後の数値を比べると効果を判断しやすくなります。なお、ここで紹介した引数はいずれも長く使われているものですが、細かな挙動はWordPressのバージョンで変わることがあるため、公式の開発者リファレンス(2026年10月現在の内容)もあわせて確認してください。
まとめ
get_posts()は内部でWP_Queryを実行し、投稿の配列だけを返す関数。違いは既定値と戻り値の形get_posts()の既定値は件数5件、suppress_filters => true、ignore_sticky_posts => true、no_found_rows => true- 件数固定の一覧は
get_posts()、ページ送りや総件数が必要ならWP_Query - テンプレートタグを使ったら
wp_reset_postdata()で現在の投稿を元に戻す - アーカイブなどメインの一覧を変えたいときは
pre_get_postsを使い、query_posts()は使わない - 多言語化プラグインとの組み合わせでは
suppress_filters => falseが必要になることがある