さて、
今回は検索結果の一覧をやりたいと思います。
つまり search.php のコードを書いて行きたいと思います。
ということで以下のようなコードになりました。
例の如くテンプレート部品化しています。
<?php
get_header();
if ( !empty( get_search_query() ) && have_posts() ) {
echo '<div class="post-list">';
while ( have_posts() ) {
the_post();
get_template_part( 'parts/content-search' );
}
echo '</div>';
get_template_part( 'parts/posts-pagination' );
} else {
get_template_part( 'parts/content-none' );
}
get_footer();
投稿一覧を出力する index.php と基本的には同様な感じになりますが、
get_search_query() にて検索キーワードが「空」になっていないか、
そして検索結果が1件以上あるかを確認する点が異なります。
検索の入力フォーム(searchform.php)の <input> に required 属性を付けてあるので、「空」入力に対してはブラウザ側で警告を出すようになるはずですので、「空」入力の確認は省くことも可能ですが、念の為にやっておくようにしました。
そのため、have_posts() が2回出てきて冗長的な感じになってしまいますが・・・(^_^;)
そして、parts/content-search.php のコードは以下のようになりました。
こちらも例の如くテンプレート部品化します。
<?php
echo '<section class="post clearfix">';
get_template_part( 'parts/search-title' );
get_template_part( 'parts/meta' );
get_template_part( 'parts/thumbnail' );
get_template_part( 'parts/search-excerpt' );
echo '</section>';
ときに、
WordPressはデータベースにMySQL系を使っているわけですが、照合順序というものがあります。
こちらに書いてあるように、ざっくり utf8_general_ci と utf8_unicode_ci の2種類があるようです。
(MySQLのより新しいバージョンではもっと増えてたりするようですが、ここでは割愛します)
WordPressでのデフォルトは utf8_general_ci になっているようですが、
こちらにあるように utf8_unicode_ci に変更することができるようです。
つまり、大文字/小文字だけでなく、ひらがな/カタカナ、全角英数/半角英数を区別しない検索が可能になります!
なので、utf8_unicode_ci に変更した方が良さげなのですが、副作用があります。
「あ/ぁ」、「ほ/ぼ/ぽ」とかも区別しなくなってしまいます。
通常仮名と捨て仮名を同一視するのはまだしも、濁点と半濁音を同一視するのはいかがなものか・・・。
「はは」で検索したら「パパ」もヒットしてしまうことになります(^_^;)
なんでこんな仕様になっているのでしょうかね。
日本語に詳しくない外国人が「文字が似てるから同じように扱って構わないだろう」という感じでやってしまったのではないかと・・・なんとなく推測する次第です(^_^;)
ということで、utf8_unicode_ci に変えるのは止めておきます。
ところで、
検索の対象はタイトルと本文になるわけですが、
検索結果の一覧においてヒットした部分を目立つように表示させたいと思います。
検索でヒットしたことをより分かりやすくアピールすることが出来ますので。
例えば↓こんな感じに。
「なんたらかんたらキーワードなんたらかんたら」
ということで、parts/search-title.php は以下のようになりました。
<?php
printf( '<h2 class="entry-title"><a href="%s">',
esc_url( get_permalink() )
);
$title = get_the_title();
// 検索キーワードとその長さ。
$query = get_search_query();
$qlen = mb_strlen( $query );
// タイトル中に検索キーワードが最初に見つかった位置を求める。
$pos = mb_stripos( $title, $query );
if ( $pos === false ) {
// 見つからなかった場合はタイトルをそのまま出力。
echo $title;
} else {
$end = mb_strlen( $title );
echo mb_substr( $title, 0, $pos );
do {
// 見つかった検索キーワードは強調。
printf( '<span class="search-highlight">%s</span>', mb_substr( $title, $pos, $qlen ) );
$pos += $qlen;
$next = mb_stripos( $title, $query, $pos );
if ( $next === false ) {
break;
}
echo mb_substr( $title, $pos, $next - $pos );
$pos = $next;
} while ( $pos < $end );
echo mb_substr( $title, $pos, $end - $pos );
}
echo '</a></h2>';
ざっくり解説してみます。
get_search_query() で検索キーワードを、get_the_title() でタイトルをそれぞれ取得します。
タイトル中に検索キーワードがヒットした部分があれば、それを強調表示するように出力します。
複数あるかもしれないので見つかる限りはループして処理します。
なお、文字エンコーディングは UTF-8 を前提としますが、日本語のようなマルチバイトな文字列を扱うために mb_ で始まる文字列関数を使うようにします。
また、
日本語特有な問題を回避するために、「WP Multibyte Patch」プラグインを導入しておくようにします。
WordPress をインストールした際におそらくデフォルトで導入されていると思いますが。
続いて、parts/search-exceprt.php は以下のようになりました。
<div class="content-excerpt">
<?php
// 抜粋の長さ。日本語の場合は単語数ではなく文字数扱いになる。
// 参照 -> wp_trim_excerpt() @ formatting.php
$len = (int)_x( '55', 'excerpt_length' ); // 日本語の場合のデフォルトは「110」になるはず。
$len = (int)apply_filters( 'excerpt_length', $len );
// 本文をテキストで取り出す。参照 -> https://www-creators.com/archives/704
$text = get_the_content('');
$text = str_replace( ' ', '', $text ); // 全角スペースは除いておく。
$text = wp_strip_all_tags( $text, true );
$text = strip_shortcodes( $text );
// 検索キーワードとその長さ。
$query = get_search_query();
$qlen = mb_strlen( $query );
// テキスト中に検索キーワードが最初に見つかった位置を求める。
$pos = mb_stripos( $text, $query );
if ( $pos === false ) {
// 見つからなかった場合は抜粋をそのまま出力。
the_excerpt();
} else {
// 見つかった位置を中心にその前後を抜粋する。
$flen = floor( ( $len - $qlen ) / 2 );
if ( $flen < $pos ) {
$pos -= $flen;
echo '…… ';
} else {
$flen = $pos;
$pos = 0;
}
$end = min( $pos + $len, mb_strlen( $text ) );
echo mb_substr( $text, $pos, $flen );
$pos += $flen;
do {
// 見つかった検索キーワードは強調。
printf( '<span class="search-highlight">%s</span>', mb_substr( $text, $pos, $qlen ) );
$pos += $qlen;
$next = mb_stripos( $text, $query, $pos );
if ( $next === false || $next + $qlen > $end ) {
break;
}
echo mb_substr( $text, $pos, $next - $pos );
$pos = $next;
} while ( $pos < $end );
echo mb_substr( $text, $pos, $end - $pos );
if ( $end < mb_strlen( $text ) ) {
echo ' ……';
}
}
?>
</div>
parts/search-title.php と同様に、検索キーワードがヒットした部分を強調出力させますが、見つかった場合は、その位置を中心にその前後を抜粋するようにしました。
抜粋の長さを得るために、wp_trim_excerpt() のコード(wp-includes/formatting.php)を参考にしました。
日本語の場合は欧文のように単語数ではなく文字数となるので注意が必要です。
当然、抜粋も文字数で切り出すことになります。
本文は get_the_content() で取得できますが、HTMLタグなどが含まれているので抜粋として処理するには都合がよくありません。
なので、こちらを参考にプレーンなテキストとして取得するようにします。
なお、自サイトでは全角スペースで段付とかやっているので、それを除外するようにしてありますが、必要なければ省いて構いません。
ということで、
検索キーワードがヒットした部分を強調させるようにしたわけですが、
HTMLタグやhrefなどの属性にヒットしてしまった場合は、抜粋から除外されてしまうのでうまくいきません。
その辺はご了承ください(^_^;)
次回に続きます。