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 が必要になることがある

お気軽にご相談ください

お見積りへ お問い合わせへ
▲