解 説

query_postsの問題点

query_postsを使用したページでページナビを設置したり、ページ送りを設置すると必ず問題が生じます。
次のページに進む事ができなかったり、404が表示されたりするものです。
*この記事は当初のものから大幅に書きかえました。

解決方法はネットに多くみられますが、どれも完璧にはいかない場合が多いですね。
色々やってみて結論として、アクションフックでpre_get_postsを使う方法が一番良さそうです

query_postsで不具合がでる理由

まずはquery_postsでうまくいかないその原因を少し探ってみたいと思います。

主な原因はquery_postsを使用するとページナビが今何ページ目かわからなくなるらしいのです。
なぜでしょうか?

簡単な例

ここではquery_postsを使用して投稿ページの表示件数を3件にする場合を例にして考察します。
ダッシュボードの設定から「1ページに表示する最大投稿数」での調整はサイトのページ全てに適用されますので使えません。
この設定はとりあえず「10件」の表示にしておくものとします。

表示件数3件はquery_postsの引数を’posts_per_page=3’とします。
コードは次のとおりです。

query_postsを使用した場合はループ終了後に忘れないようにwp_reset_query()でリセットをします。
これで正しく投稿ページが3件表示されます。
けれども、実際の投稿ページは3件以上ありますので、ページ送り機能が必要になります。

ページ送り機能はループの外に次のコードを記述します。前のコードの下に記述します。

これで、古い記事へのリンクが表示されます。
ところが、ここで問題が発生します
リンクをたどっても同じページが表示されてうまく動きません。

原因

原因を探ってみます。
pagedが原因しているらしいのですが、そもそもpagedとは何でしょうか。
pagedはグローバル変数$wp_queryの中にあります。$wp_queryを確認するにはvar_dump()を使います。

次の画像はquery_posts(‘posts_per_page=3’)を使用する前に$wp_queryをvar_dump()で表示した内容の一部です。
上の方にpagedを確認することができます。
paged1

次の画像はquery_posts(‘posts_per_page=3’)を使用後の$wp_queryをvar_dump()で表示した内容の一部です。
paged2
pagedを確認することができません。

つまりquery_postsを使用するとpagedが効かない状態になり、今自分が何ページにいるのかはわからない状態になります。
そのため、たとえwp_reset_query()でリセットを行っても最初のpagedを呼び出すだけで中身のないものになります。

pagedの状態を確認

pagedの状態を確認するには

を使用します。これでpagedの中身を知ることができます。
現在が1ページ目ならpagedは0になります。(1ではない)
2ページは2
3ページは3
と表示されます。
query_posts(‘posts_per_page=3’)の前後やループ内、ループ終了後などにget_query_var(‘paged’)を設置して値を確認します。

query_posts(‘posts_per_page=3’);の前には正しいpagedの値が取れています。まだquery_postsを使用してないのですから当然です。
ループ内は常にpagedは0になります。pagedが効いてない状態です。(これが問題の原因です!)
リセット後は再び最初と同じpagedの値がとれます。これもリセットしたから当然最初の状態になっているのです。

解決方法

以下参考例です。

次のように記述することもできます。

この設定を行うとループ内も正しく現在のページ数が表示されるようになります。

更なる問題点

ただ、この仕組みを使うとarchive.phpで再び不具合がでます。
どの月を選択しても、またどのカテゴリを選択しても最初のグループが表示されます。

どうやらこれはいきなり冒頭でquery_postsでクエリを書きかえるのが悪いようです。

結論

メインクエリの変更をquery_postsで行うと泥沼状態になる。

根本的な解決策

アクションフックを利用して必要なときに必要なデータを書きかえることです。

解決としてfunctions.phpに以下のコードを書きます。
ここでは、home.phpとarchive.phpで適用するものとします。

これはアクションフックを使用して$wp_queryの中のオブジェクト$queryに’posts_per_page’を3とセットしています。
つまり、pre_get_postsが行われると動的に$wp_queryを変更しています。
pre_get_postsフィルターとは、WordPressがクエリーを取得する前に必ず実行されるフックです。
set()はWP_Queryオブジェクトに含まれるメソッドでセッターの役割があります。

$query->set( ‘パラメーター名’, ‘パラメーターの値’ );

パラメータはWP_Queryのパラメータを参考にします。

もちろん以下の部分は必要なくなりますので削除しておきます。

注意:pre_get_postsはWP_Queryがセットアップされる前に実行されます。WP_Queryに依存するいくつかのテンプレートタグや条件分岐関数は動作しません。例えば、is_front_page()は動作しません。

完成したシンプルコード 
投稿を3件だけ表示するhome.phpとarchive.php

functions.phpの記述