サイト内検索のカスタマイズ

ちょっとサイト内検索周りを調べていたのでメモ。

やりたいこと

  1. サイト内検索の検索対象(投稿・固定ページ・カスタム投稿など)を設定したい。
  2. サイト内検索の検索ワードを取得して保存したい。
  3. プラグインで実装したい。

この3つを実現したい。1番目の内容は、現在公開しているプラグイン「WordPress Default Widget Extension」の拡張機能としてサイト内検索ウィジェットに実装したい。
2番目の内容は新しいプラグインの機能として調べてみて3番目の内容になるプラグインで実装というところにもっていきたい。

アクションフック「pre_get_posts」

何はともあれフックポイントを調べようと思い、検索すると最初にpre_get_postsというアクションフックを発見。Codexの解説を読んで見ることに。以下説明から引用。

このフックはクエリ変数オブジェクトの生成後、実際にクエリが実行される前に呼び出されます。
pre_get_posts アクションは $query オブジェクトへの参照によるアクセス手段を開発者に提供します($query への変更はオリジナルのオブジェクトに直接反映されます – 返り値は必要ありません)。

Codex日本語版クラスリファレンス/pre_get_postsの説明より引用

「$queryオブジェクトへの参照」とあるので、pre_get_postのアクションフックのコールバック関数で参照できるということか。とりあえず$queryの内容を確認するためにダンプしてみる。

public function your_function_name ( $query ) {
	var_dump( $query );
}
add_action( 'pre_get_posts', 'your_function_name' );

object(WP_Query)
あ、WP_Queryオブジェクトが表示された。なるほど、この値を使ってサイト内検索結果ページ(is_search)かどうか判断すればいいのか。以下、抜粋。

object(WP_Query)[547]
  public 'query' => 
    array (size=1)
      's' => string 'World' (length=5)
・
・
・
public 'is_search' => boolean true
・
・

お、しかしそれだけでは不十分のようで、これまたCodexに記載がある。以下一部を引用。

このフィルターは管理画面のクエリに影響を与えるのにも使えます。変更が「投稿一覧」画面に影響を与えていないか確認してください。例えば、is_main_query() と is_post_type_archive( ‘movie’ ) をチェックするだけだと、管理画面のクエリ edit.php?post_type=movie も変更してしまいます。これを避けるには ! is_admin() もチェックしてください。

Codex日本語版クラスリファレンス/pre_get_postsの「管理画面での使われ方」より引用

つまりメインクエリであり管理画面でない状態で、サイト内検索が行われたら○○という分岐処理を行えばいいのね。ふむふむ。ご丁寧にちゃんとサンプルが掲載されている。すばらしい。

if ( !is_admin() && $query->is_main_query() ) {
	if ( $query->is_search ) {
		// サイト内検索が行われた後の処理
	}
}

できた!\(^o^)/
あとはやりたい処理それぞれを足してあげればできるな。では実装準備。

サイト内検索の対象タイプをカスタマイズする方法

対象にする投稿タイプを設定できる$query->set()で対応する。

public function your_function_name ( $query ) {
	if ( !is_admin() && $query->is_main_query() ) {
		if ( $query->is_search ) {
			$query->set( 'post_type', array( 'post', 'movie' ) );
		}
	}
}
add_action( 'pre_get_posts', 'your_function_name' );

デフォルトは投稿(post)と固定ページ(page)なので$query->set()でpost_typeに投稿対応を配列で渡してあげればOK。実際にやりたい処理はウィジェット側で検索対象にしたい投稿タイプを選択できるようにするので、チェックした投稿タイプを配列にして渡してあげれば良さそう。

サイト内検索の検索文字列を取得する方法

searchform.php内でも使用されている、get_search_query()で取得する。

public function your_function_name ( $query ) {
	if ( !is_admin() && $query->is_main_query() ) {
		if ( $query->is_search ) {
			$search_word = mb_convert_kana( get_search_query(), "s", "UTF-8" );
			$args = explode( " ", $search_word );

			foreach ( $args as $value ) {
				// $valueに入っている検索ワードを登録する処理を書く
			}
		}
	}
}
add_action( 'pre_get_posts', 'your_function_name' );

mb_convert_kana()で全角スペースを半角スペースに変換。複合検索に対応するためのもの。その後、explode()関数で、配列に変換してその配列の中身を登録していけばOK。
ここで、get_search_query()で良いのかという若干の疑問…….。$queryに既にパラメータ(s)の値が入っているので、そちらをそのまま使ったほうが効率が良いのか。悩む。get_search_query()の中にはフックポイントが仕掛けられているから、それを残したほうが良いか。うん、そうしよう。