なぜ固定ページでquery_postsを使うのはよくないのか?
- 公開日 : 2015年9月2日
- カテゴリ : WordPress
WordPressを使用していてループを実現する時にquery_postsを使うことは多いかと思われます。
しかし、最近ではquery_postsを使うのはあまり良くないと言われている。
よくあるサイト構成の例
上画像ようなケースの場合、トップページ直下にある「商品紹介」「会社概要」「採用情報」「お問い合わせ」「ブログ一覧」の5つのページをすべて固定ページとして作っておく。
そして「ブログ一覧」ページの中でquery_postsを使ってループとページング(正式にはページネーションと言うらしい)をすることが考えられる。
その時のコードはおそらく以下のような感じになるのではないでしょうか?
<?php query_posts(array('posts_per_page' => 表示する数, 'paged' => get_query_var('paged'))); ?>
<?php if(have_posts()): while(have_posts()): the_post(); ?>
~ループさせるコードをここに書く~
<?php endwhile; endif; ?>
<?php wp_reset_query(); ?>
実際、私もちょっと前まで上のようにやっていた。
なぜなら、私が今まで読んだWordPressの技術書、例えば以下の本など、
- WordPressデザインブック3.x対応
(2011年9月発売)
- 本格ビジネスサイトを作りながら学ぶ WordPressの教科書
(2012年3月発売)
- 本格ビジネスサイトを作りながら学ぶ WordPressの教科書2
(2013年8月発売)
これらの本では上記のように固定ページでquery_postsを使用していた。
でも、2015年現在ではこのやり方はあまり良くないらしい。
というよりも、そもそもquery_postsを使うこと自体が良くないと言われている。
そんなこんなで、フォーラムにもたびたび投稿されるquery_posts由来のトラブル。まさにquery_posts狂想曲。引用元:query_postsを捨てよ、pre_get_postsを使おう【追記あり】【報告あり】
お祭り好きなこの私も当然ながらこの騒ぎに参加し、以下のようなことを言いながらどんちゃん騒ぎをしたものだ。
- ページングが正常に機能しない。
- query_postsの引数にarrayの配列形式だとどう書けばいいの?
- 一つのページ内にループを2つ作ると、2つめがおかしくなる。
- そもそもメインループとかメインクエリって何だよ?
そんなことを言いながら多くのサイトを見て回って調べた結果、上記の問題点はすべて解決でき、query_postsを使ってのループ表示やページ送りは固定ページやアーカイブページでも正常に行うことができていた。
しかし!
正常に表示や機能をしていても、query_postsの問題点はまだあった。
query_postsがよくない理由:データベースの重複読み込み
WordPressの仕組みでは、読み込まれるURLを元にそれが固定ページなのか投稿ページなのかそれともアーカイブページなのかなどを判別し、その判別を元にデータベースから該当するデータを読み出すことになる。
そしてその後に該当するテンプレートファイルを選択して画面に表示させる。
参考ページ:クエリ概要 – WordPress Codex 日本語版
つまり、query_postsが記述されているテンプレートファイルが選択される時点で、すでにデータベースのデータは読み込まれてしまっていることになる。
であるから、テンプレートファイル内でquery_postsによって再びデータベースを読み込み直すのはパフォーマンス面で無駄である。
get_postsでquery_postsと同じことは実現できるか?
そこで私が試したのがget_postsでquery_postsと同じループ表示をできるかということである。
これが実現できれば、今後はもうquery_postsを使わなくて済む。
そして色々調べて試してみた結果、
get_postsによってループ表示はできる。
しかし、ページング(ページネーション)はできない。
という結論に至った。(私が調べた範囲では)
つまりget_postsはquery_postsの完全な代替にはならないが、ページングを行わないループ表示に使うと良い。
(例:このブログのサイドバーにある「最近書いた記事」のように)
pre_get_postsでページング(ページネーション)を行えるか?
次に私が試したのがpre_get_postsによってループ表示とページングを実現できるか、ということである。
上記引用元のページや他いくつかのサイトを参考に試した結果、
pre_get_postsによってループ表示とページング(ページネーション)は実現可能。
であった。
その際のコードは以下のような感じである
function change_posts_per_page($query){
// 管理画面またはメインクエリでない場合は何もせず終了
if(is_admin() || ! $query->is_main_query()){
return;
}
//home.phpまたはアーカイブページまたはsearch.phpの場合は10件表示する
if($query->is_home() || $query->is_archive() || $query->is_search()){
$query->set('posts_per_page', '10');
return;
}
}
add_action('pre_get_posts', 'change_posts_per_page');
function.phpおよびpre_get_postsフックはクエリーが実行される前に呼び出されるので、query_postsのようにデータベースの重複読み込みは発生しない。
参考ページ:プラグイン API/アクションフック一覧/pre get posts – WordPress Codex 日本語版
実際にpre_get_postsを使うようになると、各ページの表示数などをfunction.phpで一元管理できるのでけっこう便利である。
現時点での最適解
ページングを行うループ表示に関してはpre_get_posts。
ページングを行わないループ表示に関してはget_posts。
となった。
もちろんquery_postsはまったく使う必要はない。
注意点:pre_get_postsは固定ページでは機能しない
注意点としては、pre_get_postsは固定ページでは機能しないことである。
つまり固定ページ内でループ表示とページングを行うことはできない。
ではどうするか?
例えば、カテゴリごとのアーカイブならcategory.phpを使えば一発ですね。
月別アーカイブならdate.phpで一発です。
それらを分けずにまとめてやりたいならarchive.phpだけでも良いですね。
参考ページ:テンプレート階層 – WordPress Codex 日本語版
しかしここで問題になるのは、すべての投稿ページのアーカイブである。
冒頭の図にある「ブログ一覧」のページのような全投稿ページのアーカイブを作りたい場合は、WordPress管理画面の「設定」→「表示設定」の中にある「フロントページの表示」を使います。
これに関しては後日また解説します。(書きました→query_postsを使わずに固定ページにすべての投稿一覧をページング表示させる方法)
またはカスタム投稿タイプを使えば、archive-カスタム投稿タイプ名.phpを使うことができるので簡単です。
このテンプレートファイルならば、この投稿タイプに属するすべての記事のアーカイブになります。
そして、この場合は前述のpre_get_posts内のif分岐の部分を、
if(is_post_type_archive()){
$query->set('posts_per_page', '5');
return;
}
のようにis_post_type_archive()を使えば投稿タイプアーカイブページであるかどうかを判定できます。
追記:このページの内容はすべて自分で調べたものなので、もし間違っている点などありましたら是非教えてください。