アーカイブテンプレートでquery_postsを使ったらページ送りで嵌った!という話。

最近カスタム投稿タイプやら、マルチサイトでのブログ一覧をいじくることがそこそこあったのですが、ちょっと注意したいことがあったのでまとめ。

query_postsとは

query_posts() 関数はメインの WordPress ループだけを変更するためのものです。新たなループを作るためのものではありません。メインループの他にループが必要な場合は、get_posts() を使ってください。メインループの他で query_posts() を使用すると、メインループが不正な状態になり期待する結果が得られません。

query_posts() 関数はページのメインクエリを上書きし、置き換えます。他の目的で使用しないでください。

query_posts() 関数は新しい WP_Query オブジェクトを作成し、グローバル wp_query 変数に割り当てます。get_posts() 関数は、グローバルエリアをまったく上書きすることなく新しい WP_Query オブジェクトを作成します。

Codex に記載されています。 pre_get_posts を使うとこんな問題を起こさずにカスタマイズ出来ます。

テンプレートタグ/query posts – WordPress Codex 日本語版

まぁ、おおざっぱにいえば、WordPressのそのページで使われているループを強制的に上書きしてしまう機能です。通常はURLを解釈して、それを元に記事を探してくるのですが、それを完全に無視します。

"トップページとかでお知らせのタイトルを5件だけ表示する"みたいなときに使うことが多いと思いますが、カスタムページテンプレートで何かの記事一覧を出したり、なんて使い方をすることもたまにあったり。

1ページ目と内容が変わらない。

よくあるトラブルその1ですね。URLには"paged/2"とか書いてあるのですが、query_postsで再設定してしまっているので、うまくいきません。

解決としては、query_postsに「今何ページ目?」という情報を与えてあげればよいので、

query_posts(array( "paged" => get_query_var('paged'), "foo" => "bar" ));

だとかしてあげればOKです。

ページが見つかりませんでしたと表示される。

今回の現象です。なぜか404になってしまいます。
上記の設定ができているのに、うまくいかないということがありました。

WordPressのテンプレート選択は、URLを解釈して読み込まれるわけですが、アーカイブページでは、最後のページが"/page/10"などの場合、"/page/11"だとかはNot Found扱いになり、404.phpが読み込まれます。これはカテゴリーでもカスタム投稿アーカイブでも同様です。

なので、query_postsをarchive.phpなどに書いていたとしても、archive.phpが読み込まれる前にページが存在しないという判定になります。

たとえば投稿が100件あって、WordPressの設定では1ページ当たりの表示件数を10件としている場合に、query_postsで5件しか表示しないようにしていると、11ページ目が存在するかしないかの判定は、WordPressの設定を使うので、すべての投稿が表示されていないのに、Not Foundとなってしまいます。

解決法1

archive.phpとかarchive-post_type.phpとかcategory.phpだとかで、メインのループを上書きしない。

テンプレートがちゃんと提供されているので、それをちゃんと素直に使いましょう。ということです。

解決法2

ただしそういうわけにもいかないときもあるとは思いますし、カスタム投稿タイプごとに表示件数もデザインも変えたい!ということはあるかと思います。

そんなときは、pre_get_posts を使いましょう。

管理画面から、投稿件数を投稿タイプとかタクソノミーとかで弄れるプラグイン作っているので、そちらもどうぞ。

まとめ

URL から得られたクエリを変更することはトラブルの元になります。条件分岐タグ のようなページの判定を組み合わせることで、URL を一切変えることなく投稿 の見せ方をカスタマイズすることができます。

とCodexにも記載されております。pre_get_posts で安全に使いましょう。