2026年5月17日
2026年5月17日
WordPressの検索機能を拡張する方法
はじめに
「WordPressの検索でカスタム投稿タイプが引っかからない」「カスタムフィールドの値も検索対象にしたい」「リアルタイムのAjax検索を実装したい」——WordPressのデフォルト検索は投稿タイトルと本文のみを対象とするため、拡張が必要なケースが多々あります。
症状・原因
WordPressの検索クエリはデフォルトでpost_type=postの投稿タイトルと本文のみを検索します。カスタム投稿タイプやwp_postmetaに保存されたカスタムフィールドは検索されません。pre_get_postsフックとカスタムJOINで検索範囲を拡張できます。
解決手順
ステップ1:カスタム投稿タイプを検索対象に含める
// functions.php: 検索クエリにカスタム投稿タイプを追加
add_action( 'pre_get_posts', function( WP_Query $query ): void {
if ( ! $query->is_search() || ! $query->is_main_query() || is_admin() ) {
return;
}
// 検索対象の投稿タイプを指定
$query->set( 'post_type', [ 'post', 'page', 'news', 'product' ] );
// 検索結果のソート順
$query->set( 'orderby', 'relevance' );
} );
// カスタム投稿タイプ登録時に exclude_from_search=false を設定
register_post_type( 'news', [
'label' => 'ニュース',
'public' => true,
'exclude_from_search' => false, // 検索対象に含める(デフォルトはpublicと同じ)
'has_archive' => true,
// ...
] );
ステップ2:カスタムフィールドを検索対象に含める
// functions.php: カスタムフィールドを検索対象に追加(JOIN + WHERE を拡張)
class My_Search_Extension {
public function __construct() {
add_filter( 'posts_join', [ $this, 'search_join' ] );
add_filter( 'posts_where', [ $this, 'search_where' ] );
add_filter( 'posts_distinct', [ $this, 'search_distinct' ] );
}
public function search_join( string $join ): string {
global $wpdb;
if ( ! is_search() ) {
return $join;
}
$join .= " LEFT JOIN {$wpdb->postmeta} AS my_meta ON {$wpdb->posts}.ID = my_meta.post_id ";
return $join;
}
public function search_where( string $where ): string {
global $wpdb;
if ( ! is_search() ) {
return $where;
}
$search_term = get_search_query();
$like = '%' . $wpdb->esc_like( $search_term ) . '%';
// カスタムフィールドの値も検索対象に追加
$where = preg_replace(
'/\(\s*' . $wpdb->posts . '\.post_title\s+LIKE\s*(\'[^\']+\')\s*\)/i',
'(' . $wpdb->posts . '.post_title LIKE $1) OR (my_meta.meta_value LIKE ' . $wpdb->prepare( '%s', $like ) . ')',
$where
);
return $where;
}
public function search_distinct( string $distinct ): string {
if ( is_search() ) {
return 'DISTINCT';
}
return $distinct;
}
}
new My_Search_Extension();
ステップ3:検索フォームをカスタマイズする
// searchform.php(テーマルート): カスタム検索フォーム
// get_search_form() はこのファイルを自動的に使用する
?>
<form role="search" method="get" class="search-form" action="<?php echo esc_url( home_url( '/' ) ); ?>">
<label class="screen-reader-text" for="search-field">
<?php esc_html_e( 'サイト内検索', 'my-theme' ); ?>
</label>
<div class="search-form__inner">
<input
type="search"
id="search-field"
class="search-form__input"
placeholder="<?php esc_attr_e( 'キーワードを入力', 'my-theme' ); ?>"
value="<?php echo esc_attr( get_search_query() ); ?>"
name="s"
autocomplete="off"
>
<!-- カテゴリーで絞り込み -->
<select name="cat" class="search-form__category">
<option value="">すべてのカテゴリー</option>
<?php
wp_dropdown_categories( [
'show_option_none' => '',
'orderby' => 'name',
'hierarchical' => true,
'hide_empty' => true,
'selected' => get_query_var( 'cat' ),
] );
?>
</select>
<button type="submit" class="search-form__submit">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="11" cy="11" r="8"/><path d="m21 21-4.35-4.35"/>
</svg>
<span class="screen-reader-text">検索</span>
</button>
</div>
</form>
ステップ4:Ajax検索(インクリメンタルサーチ)を実装する
// functions.php: Ajax検索ハンドラー
add_action( 'wp_ajax_ajax_search', 'my_ajax_search' );
add_action( 'wp_ajax_nopriv_ajax_search', 'my_ajax_search' );
function my_ajax_search(): void {
check_ajax_referer( 'ajax_search_nonce', 'nonce' );
$search = sanitize_text_field( wp_unslash( $_GET['q'] ?? '' ) );
if ( mb_strlen( $search ) < 2 ) {
wp_send_json_success( [] );
}
$query = new WP_Query( [
'post_type' => [ 'post', 'page', 'news' ],
'posts_per_page' => 8,
's' => $search,
'no_found_rows' => true,
] );
$results = [];
while ( $query->have_posts() ) {
$query->the_post();
$results[] = [
'title' => get_the_title(),
'url' => get_permalink(),
'excerpt' => wp_trim_words( get_the_excerpt(), 20 ),
'thumbnail' => get_the_post_thumbnail_url( null, 'thumbnail' ) ?: '',
'type' => get_post_type_object( get_post_type() )->labels->singular_name,
];
}
wp_reset_postdata();
wp_send_json_success( $results );
}
// assets/js/ajax-search.js
const input = document.getElementById('search-field');
if (input) {
let timer;
const resultsBox = document.getElementById('search-results');
input.addEventListener('input', () => {
clearTimeout(timer);
const q = input.value.trim();
if (q.length < 2) { resultsBox.innerHTML = ''; return; }
timer = setTimeout(async () => {
const params = new URLSearchParams({ action: 'ajax_search', nonce: mySearch.nonce, q });
const res = await fetch(`${mySearch.ajaxUrl}?${params}`);
const data = await res.json();
resultsBox.innerHTML = data.data.map(item => `
<a href="${item.url}" class="search-result">
<span class="search-result__title">${item.title}</span>
<span class="search-result__type">${item.type}</span>
</a>
`).join('') || '<p class="search-result--empty">結果が見つかりませんでした</p>';
}, 300);
});
}
ステップ5:検索結果ページをカスタマイズする
// search.php: 検索結果ページのカスタマイズ
get_header();
?>
<main id="main">
<header class="search-header">
<h1>
<?php
printf(
'「%s」の検索結果(%d件)',
esc_html( get_search_query() ),
(int) $wp_query->found_posts
);
?>
</h1>
<?php get_search_form(); ?>
</header>
<?php if ( have_posts() ) : ?>
<div class="search-results">
<?php while ( have_posts() ) : the_post(); ?>
<article class="search-result-item">
<h2><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h2>
<p class="search-result-item__meta">
<span><?php the_time( 'Y年m月d日' ); ?></span>
<span><?php echo esc_html( get_post_type_object( get_post_type() )->labels->singular_name ); ?></span>
</p>
<p><?php echo wp_trim_words( get_the_excerpt(), 30 ); ?></p>
</article>
<?php endwhile; ?>
</div>
<?php the_posts_pagination(); ?>
<?php else : ?>
<p>「<?php echo esc_html( get_search_query() ); ?>」に一致する結果は見つかりませんでした。</p>
<?php endif; ?>
</main>
<?php
get_footer();
注意事項
posts_joinとposts_whereフィルターはすべてのクエリに影響するため、必ずis_search()で検索時のみ動作するように制限してください。- カスタムフィールドのJOIN拡張はクエリが重くなります。件数が多いサイトでは
SearchWPなどの専用検索プラグインの使用を検討してください。 - Ajax検索では
no_found_rows=trueを設定してSQL_CALC_FOUND_ROWSを無効化し、レスポンスを高速化してください。
まとめ
検索機能の拡張は「pre_get_postsでカスタム投稿タイプをpost_typeに追加→posts_join/posts_whereフィルターでカスタムフィールドを検索対象に→searchform.phpでフォームをカスタマイズ→wp_ajax_フックでAjaxインクリメンタルサーチ→search.phpで件数表示と投稿タイプラベルを追加」の流れで整備します。関連記事:WordPressのページネーションをカスタマイズする方法、WordPressのカスタムフィールドをプラグインなしで実装する方法。