| ブログトップ | CMS関連 |

WordPressの検索機能をカスタマイズ

WordPressの検索の欠点として

  • カスタムフィールドが対象外
  • カテゴリー・タグ(タクソノミー)が対象外

というものがあります。

最近は内容フィールドは使用せず、カスタムフィールドに中身を入れるといった場合もあるので、これでは大変不便です。

ということで、検索機能をカスタマイズし、これらからも引っ張ってくるようにします。(ついでに抜粋からも)

なお、検索処理のカスタマイズは「pre_get_posts」をフックした関数にて行います。

pre_get_postsについては、こちらをご覧ください(query_postsを捨てよ、pre_get_postsを使おう【追記あり】【報告あり】)。

functions.phpに下記を追加します。
※pre_get_postsをフックした関数がすでに存在していている場合は、うまく編集してください。

function my_pre_get_posts($query) {
	//管理画面には適応しない
	if ( is_admin() || ! $query->is_main_query() )
		return;
		
	//検索画面の場合
	if($query->is_search()){
		global $wpdb;
		
		$keywords = get_search_query();
		if($keywords){
			
			$keywords = str_replace(" ", " ", $keywords);
			$keyword_array = explode(" ", $keywords);
			$post_ids = array();
			$post_ids_exists = array();
			foreach($keyword_array as $keyword){
				$post_ids_meta = array();	//カスタムフィールド
				$post_ids_post = array();	//タイトル・内容
				$post_ids_term = array();	//カテゴリー
				
				if($keyword){
					$keyword = '%' . like_escape( $keyword ) . '%';
					
					//カスタムフィールド内を検索
					$post_ids_meta = $wpdb->get_col( $wpdb->prepare( "
						SELECT DISTINCT post_id FROM {$wpdb->postmeta}
						WHERE meta_value LIKE '%s'
					", $keyword ) );
					
					//タイトルと内容・抜粋から検索
					$post_ids_post = $wpdb->get_col( $wpdb->prepare( "
						SELECT DISTINCT ID FROM {$wpdb->posts}
						WHERE post_title LIKE '%s'
						OR post_content LIKE '%s'
						OR post_excerpt LIKE '%s'
					", $keyword, $keyword, $keyword ) );
					
					//カテゴリー名から検索
					$term_ids = $wpdb->get_col( $wpdb->prepare( "
						SELECT DISTINCT term_id FROM {$wpdb->terms}
						WHERE name LIKE '%s'
					", $keyword) );
					//ヒットしたカテゴリーが所属する投稿
					if($term_ids){
						$term_id = implode(",", $term_ids);
						$post_ids_term = $wpdb->get_col( $wpdb->prepare( "
							SELECT DISTINCT  object_id FROM {$wpdb->term_relationships}
							WHERE term_taxonomy_id IN (%s) 
						", $term_id) );
					}
					
					//各処理で取得した投稿IDをマージする。
					$post_ids_tmp = array();
					$post_ids_tmp = array_merge($post_ids_meta, $post_ids_post, $post_ids_term);
					
					//重複している投稿IDのみ抜き出す(AND検索のため)
					if($post_ids_tmp){
						//検索ワード1つ目の場合、$post_ids_existsのキーに投稿IDを入れていく(内容はtrue)
						if(empty($post_ids_exists)){
							foreach($post_ids_tmp as $post_id){
								$post_ids_exists[$post_id] = 1;
							}
						}
						//検索ワード2つ目以降の場合
						else{
							//○つ目の投稿IDを$post_ids_exists_tmpキーに入れていく(内容はtrue)
							$post_ids_exists_tmp = array();
							foreach($post_ids_tmp as $post_id){
								$post_ids_exists_tmp[$post_id] = 1;
							}
							
							//1つ目の$post_ids_existsと、○つ目の$post_ids_exists_tmpとを比較し、存在しない場合はunsetする。
							foreach($post_ids_exists as $post_id => $value){
								if($post_ids_exists_tmp[$post_id] != 1){
									unset($post_ids_exists[$post_id]);
								}
							}
						}
					}
				}
			}
			
			//まとめた$post_ids_existsのキー(投稿ID)を、内容に移す
			if($post_ids_exists){
				foreach($post_ids_exists as $post_id => $value){
					$post_ids[] = $post_id;
				}
			}
			if($post_ids){
				$query->set('post__in', $post_ids);
			}else{
				$query->set('post__in', array(0));
			}
			
			
		}else{
			$query->set('post__in', array(0));
		}
		$query->set('s', '');	//本来の検索は邪魔になるので削除しておく
	}
}
add_action( 'pre_get_posts', 'my_pre_get_posts' );

最後に、

$query->set('s', '');

を行っているため、検索結果ページで、$sやget_query_var('s')から検索ワードを取得できなくなっています。

そのため、search.phpのget_header()の前に

$wp_query->query_vars["s"] = $wp_query->query["s"];

を追記してください。
これで$sやget_query_var('s')を使って検索ワードを取得できるようになります。

しかし結構ごつい処理をしてるので重くなりそうな気も・・・大丈夫かな。

参考サイト

このエントリーをはてなブックマークに追加

LINEで送る

nandani | 2014年12月17日 | コメント(2) | トラックバック(0) | CMS関連

トラックバック

トラックバックURL

コメント一覧

1. Posted by santo   2015年03月13日

こんにちは。こちらのコードを参考にしているのですが1点どうしても分からない箇所があります。
キーワードを2つ以上入力し検索すると
phpエラーで「Notice: Undefined offset:」がでます。

例・「あああ いいい」

一応、検索結果は無事表示されていますがどうしても修正ができません。
よろしくお願いします。

2. Posted by 総合管理者   2015年03月15日

コメントありがとうございます。
Noticeエラーはいずれかの変数の初期化が出来ておらず、引数や条件文、出力などで突然現われた場合に表示されます。
致命的ではない、というか実行には影響がないので、いつもスルーしているんですよね~。
どこだろう。
探しておきます。

なお、このエラーはphp.iniや、PHPの
ini_set( 'display_errors', 0 );
で消せますので、とりあえずそちらの方法で修正してみてください。
↑に書きましたが、Noticeエラーは実行には全く影響はありませんので。

コメントする

※メールアドレスが公開されることはありません。

名前 *
メール*
URL
Copyright(c) 2010 - 2017 ダリの雑記