【WordPressの「検索結果を投稿だけ」表示させる】「pre_get_posts」を使ってCodexのコードをこう書くと問題を回避できます

WordPressにはウィジェットとして、下のような検索フォームが追加できるようになってるじゃないですか。

設置したら、こんな感じで表示されます。(Twenty Twelveテーマを使った場合)

それで、便利なんですけど、そのままだと検索結果に固定ページも含まれてしまいますね。私のこのブログで言うと、aboutページプラグイン解説ページなどがそうです。「・・・検索結果にページは表示しなくてもいいなぁ~」と思われた方は、検索結果で固定ページの表示を除外しましょう。

検索結果で「投稿のみ」表示させる

Codexの「Plugin API/Action Reference/pre get posts」に、

サンプルコードが掲載されています。下のコードを「functions.php」に追記しましょう。

function SearchFilter($query) {
 if ($query->is_search) { //検索結果ページが表示されている場合
  $query->set('post_type', 'post'); //投稿タイプが投稿のみ
 }
 return $query;
}

add_action('pre_get_posts','SearchFilter'); //アクションフック

やっていること

WordPressに造詣の深いというか、マニアというか、そんな方々の中では何かと話題っぽい「pre_get_posts」を使ってやってますね。検索結果ページが表示されている時は、投稿タイプが投稿のものだけ表示させようってことですね。

そのままですね。

これでOK!と行きたいところですが、コード変更のアドバイスをいただきました

上に紹介したコードを使うと、いくつかの問題があることを理解しました。それを回避するための4つのアドバイスを、@jim0912さんから教えていただきました。(アイコンで、どなたか一発でわかってしまいますねw)

具体的にどういう問題があるのか。以下を読んでどこがまずいのか。まずいところをどうやって解決しているのか、見ましょう。

1.WordPressの管理画面の「固定ページを検索」のところが「問題」

今までこれ、使ったことがなかったんで知らなかったんですがね・・・下の画像をご覧ください。WordPressの管理画面に「固定ページを検索」って箇所があります。

入力欄に「about」と入れます。

すると、検索結果が下のようになります。

固定ページを検索したいのに、投稿が表示されてる。これは問題

というわけでして、このままでは管理画面上の表示結果にも影響が出ますね。そこで。いただいだアドバイスは以下です。

pre_get_posts のクエリーは、管理画面の記事一覧の表示などにも適用されるので、

判別条件に「管理画面でなければ」( ! is_admin())を加えた方がよいです。

例えば、ご呈示のコードですと、管理画面の固定ページ一覧で、固定ページの検索ができなくなってしまいます。

なるほど・・・「!is_admin()」を追記することで、これは解消できるようです。

2.「pre_get_posts」フックによる影響が広範囲だという「問題」

頂いたアドバイスが的確ですので、そのまま掲載しますと・・・

pre_get_posts フックは、メインクエリー以外にも、ナビゲーションメニューの表示や、

get_posts などによる記事抽出の際にも適用されます。

検索結果のみなど、メインクエリーにのみ影響を及ぼしたい場合は、is_main_query() を判別条件として、

影響範囲を限定させた方が意図しない不具合を制限できます。
http://codex.wordpress.org/Function_Reference/is_main_query

なるほど・・・「pre_get_posts」を使うところを、明示的に指定することで回避できますね。今回は、検索結果だけ表示を変更できればよいです。なので、メインクエリーにあたる部分ですね。「is_main_query()」を追記することが望ましい、ということですね。

3.「$query->is_search()」を使ったほうが、後々のことを考えると「よい」

これも、頂いたアドバイスをそのまま掲載いたしますと・・・

$query->is_search でも結果としては同一となりますが、

$query->is_search は、オブジェクトのメンバー変数の参照となります。

$query->is_search() としてメソッドを利用した方が、内部の実装が変更された際にも影響を受けにくい

(おそらく後方互換となりやすい)ため、メソッドを利用した方が望ましいと思われます。

これはとてもよくわかりました。なので、「$query->is_search()」に書き換えましょう。

4.結果を返すコード「return」は「要らない」

えっと・・ここも頂いたアドバイスを掲載しますと・・・・

pre_get_posts フックは、
do_action_ref_array(‘pre_get_posts’, array(&$this));
となっており、WP_Queryのオブジェクト自体を参照で受け取ります。
このため、フック内で set すれば、その時点で適用されるため、return する必要は一切ありません。

「pre_get_postsフックは、do_action_ref_array(‘pre_get_posts’, array(&$this));・・・」

のところを実際に見たかったので、query.php in tags/3.5.1/wp-includes – WordPress Trac を探すと、「pre_get_posts」に関するコード部分を確認しました。以下のようにありますが、このことかな・・・

/**
1908 * Retrieve the posts based on query variables.
1909 *
1910 * There are a few filters and actions that can be used to modify the post
1911 * database query.
1912 *
1913 * @since 1.5.0
1914 * @access public
1915 * @uses do_action_ref_array() Calls 'pre_get_posts' hook before retrieving posts.
1916 *
1917 * @return array List of posts.
1918 */
1919 function get_posts() {
1920 global $wpdb, $user_ID, $_wp_using_ext_object_cache;
1921 
1922 $this->parse_query();
1923 
1924 do_action_ref_array('pre_get_posts', array(&$this));
1925 
1926 // Shorthand.
1927 $q = &$this->query_vars;
1928 
1929 // Fill again in case pre_get_posts unset some vars.
1930 $q = $this->fill_query_vars($q);
1931 
1932 // Parse meta query
1933 $this->meta_query = new WP_Meta_Query();
1934 $this->meta_query->parse_query_vars( $q );
1935 
1936 // Set a flag if a pre_get_posts hook changed the query vars.
1937 $hash = md5( serialize( $this->query_vars ) );
1938 if ( $hash != $this->query_vars_hash ) {
1939 $this->query_vars_changed = true;
1940 $this->query_vars_hash = $hash;
1941 }
1942 unset($hash);

コードについては現状ではここまでの理解ですが、アドバイスの内容は理解できます。「return $query;」のコードは削除しましょう。

以上を踏まえて、先ほど書いたCodexのコードを下のように変更しましょう。

アドバイスの内容を理解したところで、このコードを使おう

ここまでいただいたアドバイスを元に、下記のコードに変更しました。

function SearchFilter($query) {
 if ( !is_admin() && $query->is_main_query() && $query->is_search() ) {
  $query->set( 'post_type', 'post' );
 }
}

add_action( 'pre_get_posts','SearchFilter' );

これでOK。

実行結果をサンプルとして、以下に掲載しておきますね。

サンプル

私のブログで「about」とフォームに入力しました。

【使用前:検索結果に「aboutページ」が入っている】

【使用後:aboutページが除外されている】

いいですねぇ~。

これがやりたかった!

続いて、わかりやすいところでWordPressの管理画面の固定ページ一覧の検索結果を見ましょう。

入力欄に「about」と入れます。

検索結果に、ちゃんと「Aboutページ」が表示されていますね。

閲覧画面側も意図したように、固定ページを除外して検索結果を表示していますし、管理画面側も本来の動作を損なわず、検索結果を表示してますね。どちらもいい感じです。

まとめ

ところで私は、この検索フォームってよく使います。特に自分のブログで、「昔あのことについて書いた気がするな~。検索しよう」と思ってよく使います。その時、いつも固定ページまで検索結果に表示されてました。

今回書いた方法は、わりと少しのカスタマイズで済みます。細かいことですけども、是非とも。

また、コードについてのアドバイスをいただきました@jim0912さんにはとても感謝しています。ありがとうございました!

著者:bouya Imamura