結論
get_posts() は内部で WP_Query を呼んでいるラッパーです。違いは「既定値」と「戻り値の形」にあります。
- 配列で結果だけ欲しい、ページネーションが不要、メインループに影響させたくない場合は get_posts()
- ループ(have_posts / the_post)で回したい、総件数(found_posts)やページ数(max_num_pages)が欲しい、クエリ系フィルターを効かせたい場合は WP_Query
get_posts() は WP_Post オブジェクト(または ID)の配列を返すだけで、ページネーションの情報は返しません。公式リファレンスにも、get_posts() は ignore_sticky_posts と no_found_rows を無視して、どちらも true に固定すると書かれています。
原因・仕組み
get_posts() が行っているのは、引数の既定値を調整してから WP_Query を作り、その posts 配列を返すことです。調整される主な既定値は次のとおりです。
| 項目 | get_posts() | WP_Query |
|---|---|---|
| 件数の引数 | numberposts(既定 5) | posts_per_page(既定は 設定>表示設定の値) |
| suppress_filters | 既定 true(フィルター無効) | 既定 false |
| no_found_rows | 常に true | 既定 false |
| ignore_sticky_posts | 常に true | 既定 false |
| 戻り値 | 投稿の配列 | WP_Query オブジェクト |
| ループ用のメソッド | なし | have_posts / the_post など |
つまり、同じ条件を渡しても、get_posts() は「軽い代わりに、総件数が分からず、フィルターも効かない」クエリになります。
手順(サンプルコード)
配列だけ欲しい場合(get_posts)
$posts = get_posts( array(
'post_type' => 'post',
'numberposts' => 5,
'orderby' => 'date',
'order' => 'DESC',
) );
foreach ( $posts as $item ) {
echo esc_html( get_the_title( $item ) ) . "n";
}
get_posts() の結果をテンプレートタグ(the_title() や the_post_thumbnail())で使うには、グローバルの $post に代入してから setup_postdata() を呼ぶ必要があります。公式リファレンスでも、setup_postdata() はグローバルの $post を代入しないので自分で行うこと、と明記されています。終わったら wp_reset_postdata() で戻します。
global $post;
foreach ( $posts as $post ) {
setup_postdata( $post );
the_title( '<h2>', '</h2>' );
}
wp_reset_postdata();
件数・ページ数も欲しい場合(WP_Query)
$paged = max( 1, (int) get_query_var( 'paged' ) );
$query = new WP_Query( array(
'post_type' => 'post',
'posts_per_page' => 3,
'paged' => $paged,
) );
echo '全 ' . (int) $query->found_posts . ' 件 / ' . (int) $query->max_num_pages . ' ページ';
while ( $query->have_posts() ) {
$query->the_post();
the_title( '<h2>', '</h2>' );
}
wp_reset_postdata();
動作確認(検証環境と結果)
環境は WordPress 7.1.2(日本語)、PHP 8.2.12、MariaDB 10.4.32、テーマ twentytwentyfive です。テスト用に公開記事を7件作り、同じカスタムフィールド(ks_t1)で絞って比較しました。実行は php C:Tempwp-testrun.php t1.php です(コードと出力は testsget-posts-vs-wp-query-difference.md に保存)。
| 確認内容 | 結果 |
|---|---|
| get_posts()(件数指定なし) | 5件(numberposts の既定) |
| get_posts() に numberposts=-1 | 7件 |
| get_posts() に posts_per_page=3 | 3件(posts_per_page も解釈される) |
| posts_where フィルターの呼び出し回数(get_posts 既定) | 0回 |
| 同(suppress_filters=false を渡した get_posts) | 1回 |
| 同(WP_Query) | 1回 |
| WP_Query(posts_per_page=3) | post_count=3、found_posts=7、max_num_pages=3 |
| WP_Query(no_found_rows=true) | post_count=3、found_posts=0、max_num_pages=0 |
| fields=ids | WP_Query も get_posts も整数のIDが返る |
| get_posts() 実行後のグローバル $post | NULL のまま |
| WP_Query の the_post() 後 | グローバル $post が書き換わる(wp_reset_postdata() が必要) |
件数の引数については、get_posts() に posts_per_page を渡しても3件になったので、実際には両方の名前が使えます。ただし WP_Query 側に numberposts を渡しても効かない点は、別記事(posts_per_page が効かない原因)で確認しています。
get_posts と WP_Query の違いの注意点
- get_posts() は既定でフィルターを無効化しますが、pre_get_posts は走ります(実測は posts_per_page が効かない原因)。サブクエリを巻き込むフックは is_main_query() で絞ってください。
- ページネーションを作るなら WP_Query を使い、get_posts() に paged を渡して総ページ数を求めようとしないでください。no_found_rows が true 固定のため、総件数が取れません。
- 多言語プラグインや並び順を変えるプラグインは、posts_where や posts_clauses などのフィルターで動くことがあります。get_posts() では効かないことがあるので、効かせたい場合は suppress_filters を false にします。
- 非推奨の query_posts() はメインクエリを書き換えるので使いません(query_posts() の使い方|説明・引数・注意点)。
get_posts と WP_Query の違いでよくあるミス
- get_posts() の結果に対して the_title() を引数なしで呼び、空になる(グローバル $post が未設定)。
- WP_Query のあとに wp_reset_postdata() を忘れ、フッターやサイドバーが別の記事の情報になる。
- get_posts() で総件数を数えようとして、found_posts が 0 のままになる。
- WP_Query に numberposts を渡して、10件(表示設定の値)が返ってしまう。
get_posts と WP_Query の違いのチェックリスト
- ループ・ページネーション・総件数が必要か。必要なら WP_Query にする。
- 配列だけでよいなら get_posts() にし、必要な列だけ fields=ids で取る。
- 結果をテンプレートタグで使うなら、$post の代入と setup_postdata()、wp_reset_postdata() をセットで書いたか。
- フィルターに依存する機能(並び順プラグインなど)を使っていないか。
get_posts と WP_Query の違いのFAQ(よくある質問)
Q. どちらが速いですか。
A. get_posts() は総件数を数えるための追加処理(no_found_rows が true の効果)を省くので、単純な取得では軽くなります。WP_Query でも no_found_rows を true にすれば同程度になります。ただし速度差を理由に選ぶより、必要な情報で選ぶほうが安全です。
Q. get_posts() でカスタム投稿タイプを取るには。
A. post_type に投稿タイプ名を渡します。既定は post なので、渡さないと投稿しか返りません。
Q. WP_Query のオブジェクトの posts は get_posts と同じですか。
A. 同じ WP_Post の配列です。実測でも、fields=ids を指定した場合に整数が返る挙動は一致しました。
筆者の見解(get_posts と WP_Query の違い)
私見では、迷ったら WP_Query を選ぶほうが、後からの仕様変更に強いと考えます。ページネーションや件数表示は、あとから要望が出やすい機能だからです。一方で、ウィジェットやショートコードの中で「最新5件のIDを配列で取るだけ」という場面なら、get_posts() に fields=ids を渡すのが簡潔です。どちらでも、グローバル変数を汚さないことを優先して書くのがよいと考えます。
get_posts と WP_Query の違いの関連項目
- get_posts() の使い方|説明・引数・注意点
- WP_Query クラスの使い方|説明・引数・注意点
- wp_reset_postdata() の使い方|説明・引数・注意点
- setup_postdata() の使い方|説明・引数・注意点
- query_posts() の使い方|説明・引数・注意点
