【WordPress】トップページに特定のカテゴリの記事を表示させないようにする(ページネーションが動かなくならないやり方)【#np2020】

WordPress query_posts()
前日ブックマークした記事やニュースをまとめた「はてなブックマークまとめ」という記事があるんですが、ほぼ毎朝更新されて少し数が多いのでトップページの記事一覧からは除外しています。右メニューに「はてなブックマークまとめ」だけの最近の投稿リストを作って、アクセスはそちらからしてもらう感じ。



間違ったやり方: query_posts() を使う

要は検索条件にカテゴリ除外の条件を追加すればいいわけなので、じゃあということで検索条件に特定のカテゴリを表示しない設定をしてみました。

ループの前にこんな感じ。

<?php query_posts('cat=-582'); ?>
<?php while ( have_posts() ) : the_post(); ?>

一見上手く動いたように見えたのですが、これには致命的な問題があってページネーションが動かなくなります。要は「条件を追加したい」と思って書いたけれど、これだと「条件を置き換える」と言うことになるんですね。おかげで条件が「特定のカテゴリを除く1ページ目」に置き換わってしまい、ページを移動しても移動してもずっと1ページ目が表示されるという状況に。



正しいやり方: pre_get_posts フックを使う

ドキュメントにこうあります。


query_posts() はページ内のメインクエリーを書き換え、新しいクエリーのインスタンスと置き換えるために使う関数としては過度に単純化され、問題が発生しやすい方法です。非効率的で(SQL クエリを再実行します)、一部の状況では適切に実行することもできません(特にページング処理)。モダンな WordPress コードではもっと安定したメソッドを使うべきです。例えばpre_get_posts フックを使った方法などです。ひとことで言うと、query_posts() は決して使うべきではありません。

(中略)

paged’ クエリー引数を適切に指定していないと、ページ送りが正しく動作しない可能性があります。

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


僕がやらかしたことがそのまま書いてありますね。「決して使うべきではありません。」はい。

というわけで正しい実装の方法は、functions.php で pre_get_posts フックを使う方法です。ドキュメントの例に従って以下を追加します。


function not_show_bookmarks_posts( $query ) {
   // トップページの場合のみ
   if ( $query->is_home() && $query->is_main_query() ) {
       $query->set('category__not_in', array(582));
   }
}
add_action( 'pre_get_posts', 'not_show_bookmarks_posts' );


これで当初の目的が果たせました。知らなかったとは言え(多分検索して辿り着いたどこかのサイトから拾ってきたんでしょう)、安易に強い関数を使ってはいけませんね。



その他参考になる記事

調べてみると、query_posts() でもパラメータをきちんと設定すれば正しく動作することも出来るようです。

トップページから一部の記事を除外する | Webエンジニアブログ

確かにこういう処理が必要なこともあるんでしょうし、どちらが直感的か?というと意見が分かれるかも知れませんが、ドキュメントで推奨されていないことは変わらないので、素直に pre_get_posts を使っておくのがいいかも知れません。