query_postsとページナビの問題 〜アクションフックでpre_get_postsを使う

WordPress
スポンサーリンク

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('&laquo; 古い記事'); ?></span>
  <span class="newpage"><?php previous_posts_link('新しい記事 &raquo;'); ?></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を確認することができます。
paged1

次の画像はquery_posts(‘posts_per_page=3’)を使用後の$wp_queryをvar_dump()で表示した内容の一部です。
paged2
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('&laquo; 古い記事'); ?></span>
  <span class="newpage"><?php previous_posts_link('新しい記事 &raquo;'); ?></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');
スポンサーリンク

お勧め書籍

AI関連

Python

JavaScript

HTML CSS関連

統計学

WordPress
スポンサーリンク
dororoをフォローする
IT工房|AI入門とWeb開発
タイトルとURLをコピーしました