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’とします。
コードは次のとおりです。
<?php query_posts('posts_per_page=3'); ?> <?php if(have_posts()): while(have_posts()): the_post(); ?> <div class="post"> <h2><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h2> <?php the_content(); ?> </div> <?php endwhile; endif; ?> <?php wp_reset_query(); ?>
query_postsを使用した場合はループ終了後に忘れないようにwp_reset_query()でリセットをします。
これで正しく投稿ページが3件表示されます。
けれども、実際の投稿ページは3件以上ありますので、ページ送り機能が必要になります。
ページ送り機能はループの外に次のコードを記述します。前のコードの下に記述します。
<p class="pagelink"> <span class="oldpage"><?php next_posts_link('« 古い記事'); ?></span> <span class="newpage"><?php previous_posts_link('新しい記事 »'); ?></span> </p>
これで、古い記事へのリンクが表示されます。
ところが、ここで問題が発生します。
リンクをたどっても同じページが表示されてうまく動きません。
原因
原因を探ってみます。
pagedが原因しているらしいのですが、そもそもpagedとは何でしょうか。
pagedはグローバル変数$wp_queryの中にあります。$wp_queryを確認するにはvar_dump()を使います。
<p><?php var_dump($wp_query); ?></p>
次の画像はquery_posts(‘posts_per_page=3’)を使用する前に$wp_queryをvar_dump()で表示した内容の一部です。
上の方にpagedを確認することができます。
次の画像はquery_posts(‘posts_per_page=3’)を使用後の$wp_queryをvar_dump()で表示した内容の一部です。
pagedを確認することができません。
つまりquery_postsを使用するとpagedが効かない状態になり、今自分が何ページにいるのかはわからない状態になります。
そのため、たとえwp_reset_query()でリセットを行っても最初のpagedを呼び出すだけで中身のないものになります。
pagedの状態を確認
pagedの状態を確認するには
<?php echo get_query_var('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の値がとれます。これもリセットしたから当然最初の状態になっているのです。
解決方法
以下参考例です。
<?php $paged = (get_query_var('paged')) ? get_query_var('paged') : 1; query_posts('posts_per_page=3&paged=' . $paged); ?>
次のように記述することもできます。
<?php $paged = (get_query_var('paged')) ? get_query_var('paged') : 1; $args = array( 'posts_per_page' => 3, 'paged' => $paged ); query_posts($args); ?>
この設定を行うとループ内も正しく現在のページ数が表示されるようになります。
更なる問題点
ただ、この仕組みを使うとarchive.phpで再び不具合がでます。
どの月を選択しても、またどのカテゴリを選択しても最初のグループが表示されます。
どうやらこれはいきなり冒頭でquery_postsでクエリを書きかえるのが悪いようです。
結論
メインクエリの変更をquery_postsで行うと泥沼状態になる。
根本的な解決策
アクションフックを利用して必要なときに必要なデータを書きかえることです。
解決としてfunctions.phpに以下のコードを書きます。
ここでは、home.phpとarchive.phpで適用するものとします。
function custom_posts($query) { if (( $query->is_home() || $query->is_archive()) && $query->is_main_query() &&! is_admin()) { $query->set('posts_per_page', 3); } } add_action('pre_get_posts', 'custom_posts');
これはアクションフックを使用して$wp_queryの中のオブジェクト$queryに’posts_per_page’を3とセットしています。
つまり、pre_get_postsが行われると動的に$wp_queryを変更しています。
pre_get_postsフィルターとは、WordPressがクエリーを取得する前に必ず実行されるフックです。
set()はWP_Queryオブジェクトに含まれるメソッドでセッターの役割があります。
$query->set( ‘パラメーター名’, ‘パラメーターの値’ );
パラメータはWP_Queryのパラメータを参考にします。
もちろん以下の部分は必要なくなりますので削除しておきます。
<?php query_posts('posts_per_page=3'); ?>
注意:pre_get_postsはWP_Queryがセットアップされる前に実行されます。WP_Queryに依存するいくつかのテンプレートタグや条件分岐関数は動作しません。例えば、is_front_page()は動作しません。
完成したシンプルコード
投稿を3件だけ表示するhome.phpとarchive.php
<?php if(have_posts()): while(have_posts()): the_post(); ?> <div class="post"> <h2><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h2> <?php the_content(); ?>//archive.phpの場合は<?php the_excerpt(); ?> </div> <?php endwhile; endif; ?> <?php wp_reset_query(); ?> <p class="pagelink"> <span class="oldpage"><?php next_posts_link('« 古い記事'); ?></span> <span class="newpage"><?php previous_posts_link('新しい記事 »'); ?></span> </p>
functions.phpの記述
function custom_posts($query) { if (( $query->is_home() || $query->is_archive()) && $query->is_main_query() &&! is_admin()) { $query->set('posts_per_page', 3); } } add_action('pre_get_posts', 'custom_posts');
コメントを投稿するにはログインしてください。