query_posts と get_posts は何が違う?

 Wordpress のテンプレートで投稿リストを取得する際に、どんな関数を使っていますか?

 Wordpressには query_posts get_posts の二種類が用意されていますが、それぞれには違いがあります。

 誤った使い方をすると「サイトが遅い」とか「ページが正しく表示されなくなる」「思ったデータと違うものが取得される…」などの問題が発生します。

大まかな違い

query_posts()get_posts()
処理速度遅い早い
返却型配列配列
テンプレート関数を使用した表示できるできない
グローバル汚染ありなし
全投稿数の取得あり(デフォルト)不可
スティッキー投稿の優先取得あり(デフォルト)不可

各項の説明

返却型

 どちらの関数でも、返却型は WP_Postオブジェクト数値型 の 配列となり、どちらも変わりません。

 数値型配列で返却されるのは、クエリパラメータを以下のように指定した場合のみとなります。

get_posts([
    # 取得するフィールドを ids にした場合は 数値配列が返却される
    'fields' => 'ids',
}) : int[] ;

 気を付けたいのは query_posts() を使用する場合は、事項で説明するWordPressのグローバル汚染が入る点です。

テンプレート関数を使用した表示

 query_posts() は返却値として投稿のデータを返しますが、それとは別にテンプレート関数を使用した、投稿の表示が行えます。

 テンプレート関数とはWordpressが用意する、便利な関数のことです。
 例) have_posts(), the_post(), the_title(), the_content() など

<!-- 投稿のタイトルを表示する -->
<h1><?php the_posts(); ?></h1>
<div>
    <!-- 投稿の本文を表示する -->
    <?php the_content(); ?>
</div>

グローバル汚染

 Wordpressはページを表示する前に、そのコンテンツに適した投稿を取得するデータベースの問い合わせ (以降、メインクエリ) が実行されます。
 このメインクエリの結果はPHPのグローバルメモリ上に保存され、各パーツの表示処理でなんども参照されます。

 query_posts() は、このメインクエリのデータが上書きしてしまう点に注意してください。

 例えば、なんらかの記事を表示するテンプレート上で query_posts() を使用し投稿の一覧の表示をしたとしましょう。

<?php the_title(); # 現在のページの投稿タイトルを表示する  ?>

<?php query_posts([]); # 異なる投稿を取得する ?>
<?php while (have_posts()) : the_post(); ?>
	<h1><?= the_title() # 異なる投稿のタイトルを表示する ?></h1>
<?php endwhile; ?>

<?php the_content(); # 現在のページの本文は表示されるか? ?>

 最後の the_content() で何が表示されるか分かりますか?答えは 「query_posts で取得した最後の投稿の本文」です。

 このようにテンプレート中で query_posts を使用すると、そのページに表示されるべきコンテンツが変わってしまうという現象が発生します。

 この問題は query_posts を使った後に wp_reset_query() を実行することで回避できます。

<?php the_title(); # 現在のページの投稿タイトルを表示する  ?>

<?php query_posts([]); # 異なる投稿を取得する ?>
<?php while (have_posts()) : the_post(); ?>
	<h1><?= the_title() # 異なる投稿のタイトルを表示する ?></h1>
<?php endwhile; ?>
<?php wp_reset_query(); # < query_posts() を参照するループの後に追加  ?>

<?php the_content(); # 現在のページの投稿本文が表示される ?>

 もし query_posts を使用したテンプレートで「なにか違う投稿が表示される?」と思ったら wp_reset_query() があるかを確認しましょう。

全投稿数の取得

 query_posts を使用すると、検索結果とは別に「指定の条件に一致する投稿」の全件数を取得します。

 この件数は、投稿一覧の「ページ送り」などに使用されますが、テンプレート中で query_posts を使用するケースではまず使いません。そしてこの件数取得は、ページ速度を明確に落とすほど遅い処理です

 この処理を省略するためには、検索パラメータに no_found_rows = true を入れてましょう。

query_posts([
    'no_found_rows' => true, # 全件数の取得処理が省略されます
});

※ get_posts では、この機能は強制的に無効になります。

スティッキー投稿の優先取得

 Wordpressの「投稿」には この投稿を先頭に固定表示 というオプション(スティッキー投稿)があります。
 query_posts は、この設定がされた投稿を先頭にして取得するという機能がありますが、これもまた遅い処理となります。

 そもそも、このスティッキー投稿という機能は「投稿」でしか有効ではなく「固定ページ」や「メディア」「カスタム投稿」などでは使用できません。

 使用できない機能のくせに明確に処理を遅くするという厄介な性質をもつのと、
 全投稿数の取得 とおなじく、テンプレート中でこの機能を使用することはまずないと思われるため、必要でない限りは確実に無効化しましょう。

query_posts([
    'ignore_sticky_posts' => true, # スティッキー投稿の優先取得を無効化
});

※ get_posts では、この機能は強制的に無効になります。

処理速度

 query_posts() は get_posts() より遅いとしていますが、その原因は 全投稿数の取得スティッキー投稿の優先取得 の処理が遅いためです。

 これらの機能を無効化すれば、処理速度は大きく変わらなくなります。

まとめ

 query_posts は多機能で、Wordpressが提供する多くの便利機能を利用可能です。
 ですが処理は遅く、それを回避するための設定を都度おこなったり、テンプレートが複雑になるなどデメリットも多くあります。

 一方で、get_posts は「シンプルで速い」というメリットの一方で便利機能が使えず、余計な命令を書き込む必要があるなどのデメリットも備えます。

 それぞれの問題をよく把握した上で、使い分けていきましょう。