WordPress 検索フォームの実装方法

WordPress 検索フォームの実装方法

WordPress でサイト内検索機能を実装するための「検索フォーム」や「検索結果ページ」の実装方法を紹介します。
例えば、登録された投稿やカスタム投稿の投稿記事を検索したい時に便利方法です。

サイト内検索機能を実装するためには、最低限以下の二つが必要になります。
1)検索フォームテンプレート(テンプレート化、または直書き)
2)検索結果ページテンプレート

この記事では順を追って「検索フォームの設置」から「検索結果ページ」の実装方法を解説します。
また、よくリクエストされるカスタマイズ方法も合わせて紹介するので、必要な機能があれば任意で追加してください。

  1. 検索フォームのテンプレートを作成
    1. get_search_form で検索フォームの設置
  2. 検索結果ページの作成
    1. 検索キーワード未入力時の対処方法
  3. 検索結果ページのタイトルをカスタマイズする
  4. 投稿タイプを指定して検索対象を絞り込む
  5. カテゴリやカスタムフィールドも検索対象にする
  6. 検索結果ページで検索フォームに検索キーワードを残す方法
  7. 検索ワードをハイライトして表示する方法
  8. 検索結果の表示件数を変更する方法
  9. 複数キーワード検索ができない時の対処方法
  10. WordPress で検索結果を日付順や順序順にする方法
    1. 検索結果を日付順で表示する方法
    2. 検索結果を日付順(降順)で表示する方法
    3. 検索結果を日付順(昇順)で表示する方法
    4. 検索結果を順序順で表示する方法
    5. 検索結果をランダムで表示する方法
  11. クロスサイトスクリプティングの対策方法

検索フォームのテンプレートを作成

まずは検索フォームエリア・テンプレートを用意します。
テーマディレクトリにテンプレートファイル「searchform.php」を作成、作ったテンプレートファイルに次のコードを追加してください。

<form method="get" id="searchform" action="<?php bloginfo('url'); ?>">
<input type="text" name="s" id="s" placeholder="SEARCH"/>
<button type="submit">検索する</button>
</form>

ここでは汎用的に使いまわせるテンプレートとして紹介していますが、フォームのコードを直接他のテンプレートに記載しても問題ありませんが、テンプレートファイルにすると色々なページで流用できるのでとても便利です。

placeholderの文言「SEARCH」 や buttonの文言「検索」は自由に変更してください。

get_search_form で検索フォームの設置

作った検索フォームのテンプレートは、関数 get_search_form で好きな場所に呼び出すことができます。
次のコードを、検索フォームを設置したいテンプレートに追加します。

<?php get_search_form(); ?>

検索結果ページの作成

次に検索結果を表示するためのページテンプレート search.php を作成します。
テーマディレクトリにテンプレートファイル「search.php」ファイルを作成、作ったテンプレートファイルに次のコードを追加します。

<?php if (have_posts()): ?>
<?php
if (isset($_GET['s']) && empty($_GET['s'])) {
echo '検索キーワード未入力'; // 検索キーワードが未入力の場合のテキストを指定
} else {
echo '“'.$_GET['s'] .'”の検索結果:'.$wp_query->found_posts .'件'; // 検索キーワードと該当件数を表示
}
?>
<ul>
<?php while(have_posts()): the_post(); ?>
<li>
<a href="<?php the_permalink(); ?>"><?php echo get_the_title(); ?></a>
</li>
<?php endwhile; ?>
</ul>
<?php else: ?>
検索されたキーワードにマッチする記事はありませんでした
<?php endif; ?>

これでひとまず完成になります。
サイト内検索の検索対象は「タイトル」「本文・抜粋」になるので、投稿のタイトルなどを入力して動作を確認してみましょう。

仮にこの状態で検索を行うと次のようなURLで検索結果が表示されます。
https://hirashimatakumi.com/?s=youtube

検索キーワード未入力時の対処方法

検索キーワードが未入力(空)の場合、検索結果には記事が「全件」表示されます。そのため get_search_query() を使用して、未入力時の分岐を追加します。
次のコードを search.php に追加してください。

<?php if(empty(get_search_query())): ?>
<!-- 検索キーワード未入力 -->
<?php else: ?>
<?php if (have_posts()): ?>
<?php while (have_posts()) : the_post(); ?>
<!-- 検索キーワードに該当する記事を表示 -->
<a href="<?php the_permalink(); ?>"><?php echo get_the_title(); ?></a>
<?php endwhile; ?>
<?php else: ?>
<!-- 検索キーワードにマッチする記事がない場合 -->
<?php endif; ?>

検索結果ページのタイトルをカスタマイズする

検索結果ページのタイトルの表示に wp_title を使用すると、定型のタイトルが表示されるので、このタイトルをカスタマイズしたい場合は次のコードを functions.php に追加します。

/*【出力カスタマイズ】検索結果のタイトルをカスタマイズ */
function wp_search_title($search_title){
if(is_search()){
$search_title = '「'.get_search_query().'」の検索結果';
}
return $search_title;
}
add_filter('wp_title','wp_search_title');

投稿タイプを指定して検索対象を絞り込む

投稿タイプを指定して検索対象を絞り込む方法を紹介します。
例えば、投稿(post)と固定ページ(page)の他に、カスタム投稿(blog)を検索対象にしたい場合、
次のコードを functions.php に追加します。

/*【出力カスタマイズ】検索対象をカスタム投稿タイプで絞り込む */
function my_pre_get_posts($query) {
if ( !is_admin() && $query->is_main_query() && $query->is_search() ) {
$query->set( 'post_type', array('post','page','blog') );
}
}
add_action( 'pre_get_posts','my_pre_get_posts' );

上記コードの4行目に記載している $query->set('post_type', array('post', 'page', 'blog')); の投稿タイプの指定を編集します。
例えばカスタム投稿タイプ「ブログ(スラッグ:blog)」を加える場合は array('post', 'page', 'blog') のようにカンマ区切りでスラッグを追加します。

register_post_type を使用している場合

カスタム投稿タイプを function.php 内で register_post_type 関数を使って追加している場合は、パラメーターに exclude_from_search を加えて検索対象に含める必要があります。
例えば、カスタム投稿タイプを検索に含める場合は register_post_type の値を false にします。

'exclude_from_search' => false, // false 検索対象に含める

逆に、検索に含めない場合は register_post_type の値を true にします。

'exclude_from_search' => true,

末尾のカンマは挿入箇所に応じて調整してください。

カテゴリやカスタムフィールドも検索対象にする

カスタムフィールドやカテゴリなどを検索対象に含める場合はプラグインを使うと簡単です。
例えば Search Everything などは導入が簡単でおすすめですが、サポートが終了しているプラグインも多いため注意が必要です。

カスタムフィールドやカテゴリを検索対象とした場合、and検索が意図した通りに動かない場合があります。実装を決める際は、検証作業を入念に行うことをおすすめします。

検索結果ページで検索フォームに検索キーワードを残す方法

検索結果ページに遷移すると検索フォームに入力されたキーワードが消えてしまいます。
最初の検索後に、キーワードを残して再検索を続ける場合もあるため、ここでは placeholder と value に細工をして、キーワードを残す対応します。
次のコードは、「検索フォームのテンプレートを作成」で紹介したコードを、キーワードが残るようにカスタマイズしています。

<form method="get" id="searchform" action="<?php bloginfo('url'); ?>">
<input type="text" name="s" id="s" placeholder="<?php if(!is_search()){ echo 'SEARCH';} ?>" value="<?php if(is_search()){ echo get_search_query();} ?>"/>
<button type="submit">検索する</button>
</form>

ここでは、input タグの placeholder と value の値にif文を追加して、検索結果ページへ遷移した後に検索キーワードを取得して代入する処理を加えています。

検索ワードをハイライトして表示する方法

検索結果ページで検索ワードをハイライトして強調表示する方法を紹介します。
例えば、検索結果ページでタイトルと本文(または抜粋)が表示される場合、表示されるテキスト内に検索キーワードが含まれていたら、mark タグで囲うようにするには、次のコードを function.php に追加します。

/*【検索カスタマイズ】検索ワードをハイライトする */
function highlight_search_word($text){ // 関数「highlight_search_word」を定義
if(is_search()){ // 検索ページが表示されている時に実行
$keys = implode('|', explode(' ', get_search_query())); // 検索ワードを分割して配列にして返す
$text = preg_replace('/('. $keys .')/iu', '<mark>
/*【検索カスタマイズ】検索ワードをハイライトする */
function highlight_search_word($text){ // 関数「highlight_search_word」を定義
if(is_search()){ // 検索ページが表示されている時に実行
$keys = implode('|', explode(' ', get_search_query())); // 検索ワードを分割して配列にして返す
$text = preg_replace('/('. $keys .')/iu', '<mark>\0</mark>', $text); // 該当ワードを mark で囲む
}
return $text; // 変更した値を呼び出し元に戻す
}
add_filter('the_title', 'highlight_search_word'); // タイトルに適用
add_filter('the_content', 'highlight_search_word'); // 本文に適用
add_filter('the_excerpt', 'highlight_search_word'); // 抜粋に適用
</mark>', $text); // 該当ワードを mark で囲む
}
return $text; // 変更した値を呼び出し元に戻す
}
add_filter('the_title', 'highlight_search_word'); // タイトルに適用
add_filter('the_content', 'highlight_search_word'); // 本文に適用
add_filter('the_excerpt', 'highlight_search_word'); // 抜粋に適用

画像のaltにタグが挿入されてしまう時の回避方法

この方法の場合、検索結果ページで表示される投稿の画像の altget_the_title() を使っていると、alt にタグが挿入されてしまいます。
これを回避するためには strip_tags() を使って、タイトルからタグを除く処理を追加します。
この記述は現象が起こる場合に、該当箇所にのみ適用してください。

<?php echo strip_tags(get_the_title()); ?>

検索結果の表示件数を変更する方法

何も設定をしない場合は管理画面の設定「表示設定」の最大投稿件数が表示件数として反映されますが、時には表示件数を変更したい場合もあるかと思います。
簡単に検索結果の表示件数を変更したい場合は query_posts() を使用します。
検索結果テンプレートの search.php の have_posts 上に query_posts() の記述を追加します。
次のコードは、検索結果の表示件数を1ページあたり12件にする方法です。

<?php query_posts($query_string.'&posts_per_page=12'); ?>
<?php if (have_posts()): ?>
-- 以下省略 --

また、function.php 他の投稿タイプと合わせて表示件巣を調整する方法もあります。

/*【出力カスタマイズ】投稿タイプ別にクエリーを設定 */
function custom_posts_per_page() {
global $wp_query;
if( $wp_query->is_admin ) return;
if( is_search() ) {
$wp_query->query_vars['posts_per_page'] = 100; // 検索結果の表示件数を指定
}
}
add_filter('pre_get_posts', 'custom_posts_per_page');

if文を追加(elseifなどで)してに他のポストタイプを指定することもできます。

複数キーワード検索ができない時の対処方法

複数のキーワード検索する時に「全角スペース」でキーワードを区切ると、全角スペースが文字扱いされて意図した検索ができない場合があります。
そんな時はプラグイン「WP Multibyte Patch」をインストールして有効化してください。
WordPress を手動でインストールするとこのプラグインのインストールを忘れがちなので気をつけましょう。

WordPress で検索結果を日付順や順序順にする方法

WordPress で検索結果を日付順や順序順にする方法を紹介します。
検索結果の順番をカスタマイズするにはフィルターフックを利用します。

検索結果を日付順で表示する方法

検索結果を日付順で表示する場合は date(post_dateでも可) を指定します。
例えば、検索結果を日付順に表示したい場合は次のコードを function.php に追加します。

/*【出力カスタマイズ】検索結果をカスタマイズ */
function my_pre_get_posts($query) {
if ( !is_admin() && $query->is_main_query() && $query->is_search() ) {
$query->set( 'orderby', 'date' ); // 日付順
}
}
add_action( 'pre_get_posts','my_pre_get_posts' );

上記のコードは先に紹介している「投稿タイプを指定して検索対象を絞り込む」と重複します。
先の投稿タイプの絞り込みを行なっている場合は以下の部分をフィルターフックに追記してください。

-- 以下省略 --
$query->set( 'orderby', 'date' ); // 日付順
-- 以下省略 --

検索結果を日付順(降順)で表示する方法

検索結果を日付順(降順)で表示する場合は order で desc を指定します。
例えば、検索結果を日付順(降順)に表示したい場合は次のコードを、先に紹介した「日付順」の項目を参考に function.php に追加します。

-- 以下省略 --
$query->set( 'order', 'DESC' ); // 最高から最低へ降順 (3, 2, 1, c, b, a)
-- 以下省略 --

検索結果を日付順(昇順)で表示する方法

検索結果を日付順(昇順)で表示する場合は order と asc を指定します。
例えば、検索結果を日付順(昇順)に表示したい場合は次のコードを、先に紹介した「日付順」の項目を参考に function.php に追加します。

-- 以下省略 --
$query->set( 'order', 'ASC' ); // 最低から最高へ昇順 (1, 2, 3, a, b, c)
-- 以下省略 --

検索結果を順序順で表示する方法

検索結果を順序順で表示する場合は menu_order を指定して対応します。
例えば、検索結果を順序順に表示したい場合は次のコードを、先に紹介した「日付順」の項目を参考に function.php に追加します。

-- 以下省略 --
$query->set( 'orderby', 'menu_order' ); // 順序順で並び替え
-- 以下省略 --

検索結果をランダムで表示する方法

検索結果をランダムで表示する場合は rand を指定して対応します。
例えば、検索結果をランダムに表示したい場合は次のコードを、先に紹介した「日付順」の項目を参考に function.php に追加します。

-- 以下省略 --
$query->set( 'orderby', 'rand' ); // ランダムで並び替え
-- 以下省略 --

クロスサイトスクリプティングの対処方法

サイトの脆弱性検査で「クロスサイトスクリプティング対策」をリクエストされた時の対処方法を紹介します。
対策をしていない場合、次のようなアラートを表示するコードをキーワードに入れて検索すると、検索結果ページで任意のプログラム(下記例の場合はアラート)が動いてしまいます。

<script>alert(1)</script>

実行するとアラート「1」が表示されます。
自身のサイトでアラートが表示される場合、悪意あるプログラムも動いてしまう可能性があるため、対処として、プログラムに使われる文字列「&」「’」「”」「<」「」「>」などを、プログラムと認識されないように htmlspecialchars() を使ってエスケープ処理をします。
例えば、検索キーワードを「$_GET[‘s’]」で取得している場合のエスケープ処理は次のようになります。

$str = $_GET['s']; // 検索文字列の取得
$e_str = htmlspecialchars($str, ENT_QUOTES, "utf-8"); // 検索文字列のエスケープ処理
echo $e_str; // 検索文字の表示

このコードを、最初に紹介した検索結果ページに組み込むと、次のようになります。

<?php
if (isset($_GET['s']) && empty($_GET['s'])) {
echo '検索キーワード未入力'; // 検索キーワードが未入力の場合のテキストを指定
} else {
$str = $_GET['s']; // 検索文字列の取得
$e_str = htmlspecialchars($str, ENT_QUOTES, "utf-8"); // 検索文字列のエスケープ処理
echo '“'.$e_str .'”の検索結果:'.$wp_query->found_posts .'件'; // 検索キーワードと該当件数を表示
}
?>

まとめ

WordPress でサイト内検索機能を実装するための「検索フォーム」や「検索結果ページ」の実装方法を紹介した。