WordPressでカスタムフィールドの値をサイト内検索の対象にする方法(プラグインなし)

WordPressのサイト内検索で「カスタムフィールド」の値を検索対象に含める方法を紹介します。

PCで検索

この記事にたどり着いた方なら重々承知していることと思いますが、困ったことにWordPressの検索はカスタムフィールドを検索対象に含めません。プラグインやら色々試行錯誤した結果、上手く動作するようになったので共有します。

WPプラグイン「Search Everything」

私が最初に試したのが、WPプラグイン「Search Everything」。解説しているサイトも多いのでそれなりに安心感もあります。プラグインで簡単に対応できるなら使わない手はないですよね。

使ってみた結果…残念ながら私の環境では重すぎて使い物になりませんでした。

ただ、今回私が導入したサイトは記事が10万件近くあり、カスタムフィールドも相当数あったため使えなかった可能性はあります。小規模サイトの検索であれば問題なく動作するかもしれないので、とりあえず試してみてもいいと思います。

LINK:Search Everything

WPプラグイン「Custom Fields Search by BestWebSoft」

次に試したのが、WPプラグイン「Custom Fields Search by BestWebSoft」

こちらは検索するフィールドも指定でき、実際上手く動いて運用していたのですが、いつぞやかのタイミングで急に動作しなくなりました。動かなくなった原因は不明ですが、サーバー側のPHPバージョンアップとかあったのでそのあたりを怪しんでいます。

現在は配布されていないようなので、残念ながら削除。

functions.phpに記述する

で、最終的に私が辿り着いた方法はfunctions.phpに追記するやり方です。

function custom_search($search, $wp_query) {
	global $wpdb;

	if (!$wp_query->is_search)
			return $search;
	if (!isset($wp_query->query_vars))
			return $search;

	$search_words = explode(' ', isset($wp_query->query_vars['s']) ? $wp_query->query_vars['s'] : '');
	if ( count($search_words) > 0 ) {
			$search = '';
			$search .= "AND post_type = 'post'";
			foreach ( $search_words as $word ) {
					if ( !empty($word) ) {
							$search_word = '%' . esc_sql( $word ) . '%';
							$search .= " AND (
								 {$wpdb->posts}.post_title LIKE '{$search_word}'
								OR {$wpdb->posts}.post_content LIKE '{$search_word}'
								OR {$wpdb->posts}.ID IN (
								SELECT distinct post_id
								FROM {$wpdb->postmeta}
								WHERE meta_value LIKE '{$search_word}'
								)
							) ";
						
					}
			}
	}
	return $search;
}
add_filter('posts_search','custom_search', 10, 2);

コードはこちらのサイトを参考にさせていただきました。

参考サイト:WordPressのサイト内検索でカスタムフィールドの内容も検索結果に含める方法

上記のコードをfunctions.phpに貼りつけるだけで問題なく動作します。よかったよかった。

それにしても、これだけ困っている人がいるのにカスタムフィールドが検索対象にならないなんていかがなものですかね。カスタムフィールド自体が拡張機能なので、色々問題があるのはわかりますが…Gutenberg(グーテンベルグ)なんて導入する前に(ry…とか思っちゃいました(笑)

2019/01/15:追記

特定のカスタムフィールドと記事本文を除外する(特定のフィールドのみ検索対象にする)場合は、下記のコードを参考にしてください。

// カスタムフィールドの内容も検索結果に含める
function cf_search_join( $join ) {
	global $wpdb;
	if ( is_search() ) {
		$join .= ' LEFT JOIN ' . $wpdb->postmeta . ' ON ' . $wpdb->posts . '.ID = ' . $wpdb->postmeta . '.post_id ';
	}
	return $join;
}
add_filter( 'posts_join', 'cf_search_join' );

function cf_search_where( $where ) {
	global $wpdb;
	if ( is_search() ) {
		$where = preg_replace(
			"/\(\s*" . $wpdb->posts . ".post_title\s+LIKE\s*(\'[^\']+\')\s*\)/",
			"(" . $wpdb->posts . ".post_title LIKE $1) OR (" . $wpdb->postmeta . ".meta_value LIKE $1)", $where );

		// 特定のカスタムフィールドを検索対象から外す
		$where .= " AND (" . $wpdb->postmeta . ".meta_key NOT LIKE 'number')";
		$where .= " AND (" . $wpdb->postmeta . ".meta_key NOT LIKE 'zip')";
		$where .= " AND (" . $wpdb->postmeta . ".meta_key NOT LIKE 'access')";
	}
	return $where;
}
add_filter( 'posts_where', 'cf_search_where' );

function cf_search_distinct( $where ) {
	global $wpdb;
	if ( is_search() ) {
		return "DISTINCT";
	}
	return $where;
}
add_filter( 'posts_distinct', 'cf_search_distinct' );

// 検索対象を『タイトルのみ』にする
function __search_by_title_only( $search, & $wp_query ) {
	global $wpdb;
	if ( empty( $search ) )
		return $search; // skip processing - no search term in query
	$q = $wp_query->query_vars;
	$n = !empty( $q[ 'exact' ] ) ? '' : '%';
	$search =
		$searchand = '';
	foreach ( ( array )$q[ 'search_terms' ] as $term ) {
		$term = esc_sql( like_escape( $term ) );
		$search .= "{$searchand}($wpdb->posts.post_title LIKE '{$n}{$term}{$n}')";
		$searchand = ' AND ';
	}
	if ( !empty( $search ) ) {
		$search = " AND ({$search}) ";
		if ( !is_user_logged_in() )
			$search .= " AND ($wpdb->posts.post_password = '') ";
	}
	return $search;
}
add_filter( 'posts_search', '__search_by_title_only', 500, 2 );

参考サイト:PHP – WordPressのフリーワード検索で特定のカスタムフィールドのみ除外したい