2026年8月30日
2026年8月30日
get_postsとquery_postsの違い|WP_Query・pre_get_postsとの使い分け
WordPress で投稿の一覧を取り出す関数には get_posts()・query_posts()・WP_Query があり、メインクエリを変える方法として pre_get_posts フックもあります。名前が似ていますが、役割は大きく違います。
結論から書くと、次のように使い分けます(2026年10月時点の developer.wordpress.org の記述にもとづく)。
| 目的 | 使うもの |
|---|---|
| サイドバーや記事下などに、別の投稿一覧を少し出す | get_posts() |
| 別の一覧を出し、ページ送りも付けたい | WP_Query |
| トップページ・アーカイブなど、もともとの一覧(メインクエリ)の条件を変えたい | pre_get_posts |
query_posts() |
使わない(既存コードは置き換える) |
query_posts()を使わない理由
公式リファレンスでは、query_posts() についてメインクエリを丸ごと上書きする関数で、プラグインやテーマでの使用は想定していないと説明し、なるべく避けるよう書かれています。代わりに、メインクエリの変更には pre_get_posts、別の一覧には WP_Query か get_posts() を勧めています。
問題になる点は次のとおりです。
- WordPress はテンプレートを読み込む前に、URL に応じたメインクエリをすでに実行しています。
query_posts()はその後でもう一度クエリを実行するため、データベースへの問い合わせが無駄に増えます。 - グローバル変数
$wp_queryを置き換えるので、条件分岐タグ(is_home()など)や、ページ送り(paged)の扱いがずれることがあります。2ページ目が404になる、同じ記事が表示される、といった不具合の原因になります。 - 使った後に
wp_reset_query()を呼ばないと、後に続く処理が上書きされたクエリのまま動きます。
古いテーマに次のようなコードがあれば、後の節の方法に置き換えます。
<?php
// 置き換え対象の例(使わない)
query_posts( 'posts_per_page=5&category_name=blog' );
get_posts()の使い方
get_posts() は、条件に合う投稿を 配列 で返す関数です。中では WP_Query を使っています。グローバルなクエリは変えないので、テンプレートのどこで使ってもメインの一覧には影響しません。
既定値
公式リファレンスに載っている既定値は次のとおりです。
| 引数 | 既定値 | 意味 |
|---|---|---|
numberposts |
5 |
取得件数。-1 で全件 |
category |
0 |
カテゴリーID(cat として渡される) |
orderby / order |
date / DESC |
新しい順 |
include / exclude |
空 | 投稿IDで含める/除く |
post_type |
post |
投稿タイプ |
suppress_filters |
true |
SQL を書き換えるフィルターを効かせない |
また、get_posts() は ignore_sticky_posts と no_found_rows を常に true にします。つまり、先頭固定表示の投稿を特別扱いせず、総件数を数えません。そのため ページ送りには向きません。
WP_Query の引数(category_name、tag、meta_query、date_query など)もそのまま渡せます。
基本の例
テンプレートタグを使わず、投稿オブジェクトから値を取り出す書き方が最も簡単です。
<?php
$recent_posts = get_posts( array(
'numberposts' => 5,
'category_name' => 'news', // カテゴリーのスラッグ
) );
if ( $recent_posts ) {
echo '<ul>';
foreach ( $recent_posts as $recent ) {
printf(
'<li><a href="%s">%s</a></li>',
esc_url( get_permalink( $recent ) ),
esc_html( get_the_title( $recent ) )
);
}
echo '</ul>';
}
the_title()などのテンプレートタグを使う場合
the_title() や the_permalink() はグローバル変数 $post を見ます。ループの中で使うときは setup_postdata() を呼び、最後に wp_reset_postdata() で元に戻します。関数の中で書く場合は global $post; が要ります。
<?php
global $post;
$recent_posts = get_posts( array( 'numberposts' => 5 ) );
foreach ( $recent_posts as $post ) {
setup_postdata( $post );
?>
<h3><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h3>
<?php
}
wp_reset_postdata();
日付で絞る例
date_query を使うと期間を指定できます。
<?php
$october = get_posts( array(
'numberposts' => -1,
'date_query' => array(
array(
'after' => '2026-10-01',
'before' => '2026-10-31',
'inclusive' => true,
),
),
) );
numberposts を -1 にすると全件を読み込みます。件数が多いサイトでは、メモリと時間を使うので、上限を決めて取得します。
注意点
suppress_filtersがtrueのため、posts_whereなどの SQL を書き換えるフィルターは効きません。多言語化プラグインなど、フィルターで絞り込む仕組みに合わせたい場合は'suppress_filters' => falseを指定します。- 利用者の入力(
$_GETなど)を引数に使う場合は、absint()やsanitize_key()などで値を整えてから渡します。
WP_Queryでページ送りつきの一覧を作る
ページ送りが必要な別の一覧は、WP_Query で作ります。max_num_pages で総ページ数がわかります。
<?php
$paged = max( 1, get_query_var( 'paged' ) );
$news = new WP_Query( array(
'post_type' => 'post',
'category_name' => 'news',
'posts_per_page' => 10,
'paged' => $paged,
) );
if ( $news->have_posts() ) {
while ( $news->have_posts() ) {
$news->the_post();
the_title( '<h3>', '</h3>' );
}
echo paginate_links( array( 'total' => $news->max_num_pages, 'current' => $paged ) );
wp_reset_postdata();
}
固定ページのテンプレートの中でこの一覧を作ると、URL の /page/2/ がメインクエリ側で解釈されて404になることがあります。トップページやアーカイブの一覧そのものを変えたいなら、次の pre_get_posts を使うほうが不具合が起きにくくなります。なお、固定ページを静的フロントページにしている場合、ページ番号は paged ではなく page(get_query_var( 'page' ))に入ります。
pre_get_postsでメインクエリを変える
トップページに「blog カテゴリーだけを5件ずつ」出したい、という目的なら、テンプレートでクエリを作り直さず、メインクエリが実行される前に条件を変えます。テーマの functions.php(子テーマ推奨)か、自作プラグインに書きます。
<?php
add_action( 'pre_get_posts', function ( $query ) {
if ( is_admin() || ! $query->is_main_query() ) {
return;
}
if ( $query->is_home() ) {
$query->set( 'category_name', 'blog' );
$query->set( 'posts_per_page', 5 );
}
} );
is_admin()で管理画面を除き、is_main_query()でメインクエリだけに限定します。これを忘れると、ウィジェットや管理画面の一覧まで変わります。- 条件分岐は
is_home()ではなく$query->is_home()のように、渡されたクエリに対して呼びます。 - テンプレート側は通常のループ(
have_posts()/the_post())のままで、ページ送りも正しく動きます。
pre_get_posts の詳しい使い方は pre_get_postsの使い方 も参考にしてください。
ブロックテーマの場合
ブロックテーマ(Twenty Twenty-Five など)では、テンプレートは PHP ではなくブロックで作ります。投稿一覧は「クエリーループ」ブロックで条件を指定でき、PHP を書かずに済む場合が多くあります。メインクエリを変えたいときは、ブロックテーマでも pre_get_posts が使えます。
まとめ
query_posts()はメインクエリを上書きするため、公式に使用を避けるよう書かれている。既存コードは置き換える。- 少数の別一覧は
get_posts()。既定は5件・新しい順・総件数を数えないので、ページ送りには使わない。 - ページ送りが必要な別一覧は
WP_Query。使った後はwp_reset_postdata()。 - トップページやアーカイブの一覧を変えるなら
pre_get_postsで、is_main_query()と! is_admin()で対象を限定する。