get_posts と WP_Query の違い|使い分けと実測結果

結論

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 の違いの関連項目

出典(一次情報)

タイトルとURLをコピーしました