WordPressのブログ内検索って……
そもそもサイト内検索の類を使ったことはほとんどないな。
……とは言っても、このブログにも検索フォームは置いてるし。
WordPressの検索機能は、Googleとかに比べると圧倒的に弱い。
特に、英数字なんかの「全角」と「半角」の違いがあると、検索にひっかかってくれない。
ということで、今回は「全角英数字」でも半角を、「半角カタカナ」でも全角を、それぞれ検索できるようにしてみた話を。
もちろん、投稿内では英数字は半角を、カタカナは全角を使用しているのが前提だ。
今回の方針
まず、多くの検索サイトと同じように、全角・半角を同一視して検索する、というのをやってみようとしたが、俺にはちょっとわからなかった。
MySQLでのクエリ実行時に、オプションとして COLLATE utf8_unicode_ci
というのを指定すればよい、ということがわかり、その辺りの WP_Query
のソースと半日睨めっこしながら試行錯誤したが、どうにもうまくいかなかった。
そこで少し妥協して、検索文字列に全角英数字が含まれれば半角に変換し、半角カタカナが含まれれば全角に変換する、という仕組みにしてみた。
普段から投稿では英数字は半角、カタカナは全角、と決めているなら基本的に問題ないが、逆に「全角英数字」と「半角カタカナ」での検索は不可能となるわけだ。
そして俺が妥協してこれを実装した頃、WordPressサポートフォーラムでこの検索の話題が出て、 utf8_unicode_ci
を使った回答が示された。
うーん、やっぱここまでやらなきゃいけないのかぁ、というか……
WP_Query::parse_search()
のソースの一部を改変したものだが、俺はこれを試していない。
ということで、今回は俺が考えた「全角・半角を変換する方法」を紹介する。
pre_get_posts アクション
メインクエリ(アーカイブ系ページの投稿一覧)を取得する条件を変えたいときは、ほとんどの場合 pre_get_posts
アクションを使う。
俺は近頃WordPressサポートフォーラムでも活動しているので、いろんなカスタマイズを目撃するが、メインクエリでやればいいのに WP_Query
だとか、よりによって query_posts()
を使おうとする人の多いこと多いこと。
完全に余談だが、 WP_Query
はメインクエリ以外で投稿の一覧的なものを取得する場合に使い、 query_posts()
はとにかく使わない!! と覚えておいてほしい。
さて、その pre_get_posts
の使い方はだいたい以下のような感じ。
function my_pre_get_posts( $query ) {
// $query はグローバル変数の $wp_query と同じもの
if ( is_admin() || ! $query->is_main_query() )
return;
if ( $query->is_search() ) {
// ここで処理
}
}
add_action( 'pre_get_posts', 'my_pre_get_posts' );
3〜4行目の記述はおまじない、というかほぼ決まり文句で、管理ページで実行した場合と、メインクエリ以外で実行した場合には、何もせずに素通りさせる。
特に、うっかり管理ページのメインクエリを弄ると不具合の元だが、あえてそうしたい場合は細心の注意を払うように。
というわけで、上のように関数をフックしていく。
ちなみに5行目のif文は、今回は検索結果アーカイブでの処理なので当然必要になる。
検索クエリを取得
検索クエリを弄るために、まずその元となる検索文字列を取得する。
クエリパラメータは WP_Query::get()
で取得できるので、上記の例においては以下のように書ける。
if ( $query->is_search() ) {
$search = $query->get('s');
}
検索結果ページのURLを変更している人は、この方法では検索クエリを取得できないかもしれない。
例えば、俺のブログでは検索結果ページのURLを /search/.... に変更しているので、以下のようにした。
if ( $query->is_search() ) {
if ( preg_match( '@^/search/([^/?]+)@', $_SERVER['REQUEST_URI'], $matches ) )
$search = urldecode($matches[1]);
}
$_SERVER
はスーパーグローバル変数で、 $_SERVER['REQUEST_URI']
で「リクエストされたURLのドメイン以下の部分」を指す。
こちらはURLからの取得なので urldecode()
でデコードする必要がなる。
全角・半角を変換
さて、検索クエリが取得できたら、これを変換してクエリを上書きする。
ここで mb_convert_kana()
という関数を利用する。
これは英数字やカタカナといった文字の全角・半角を変換するという、日本語環境向けの関数といえるものだ。
第1引数に変換したい文字列、第2引数に変換のしかたを定めた文字列値を指定する。
第2引数の指定のしかたについては各自調べてほしいが、俺が今回使ったのは以下のような感じだ。
$search = mb_convert_kana($search, 'asKV');
'a'
は「全角英数字」を半角に、 's'
は「全角スペース」を半角に、 'K'
は「半角カタカナ」を全角に、 'V'
は 'K'
と共に使用することで、半角カタカナの濁点・半濁点をそのまま全角に置き換えず、直前の文字に付ける、という変換を指定する。
※これらの文字は連結して 'asKV'
とする。
このように検索クエリの全角・半角を整えてからメインクエリに渡す、というわけだ。
メインクエリにパラメータを追加・上書きするには WP_Query::set()
を使い、以下のように書く。
$query->set('s', $search);
さて、これまでのコードをまとめると、以下のようになる。
function my_pre_get_posts( $query ) {
if ( is_admin() || ! $query->is_main_query() )
return;
if ( $query->is_search() ) {
$search = $query->get('s');
$search = mb_convert_kana($search, 'asKV');
$query->set('s', $search);
}
}
add_action( 'pre_get_posts', 'my_pre_get_posts' );
なお、注意点というほどでもないかもしれないが、このコードを使用すると、header.php のようなテーマファイルで get_query_var('s')
なんかを使って検索クエリを取得しようとすると、変換後の文字列が返ってきてしまう。
元の(リクエスト通りの)検索クエリを得るには、URLから取得するべし。
例えば $_GET['s']
とか、上記の $_SERVER
を使った正規表現とか。
まとめ
今回は、検索クエリ文字列の全角・半角を mb_convert_kana()
を使って変換することで、全角英数字、半角カタカナで検索できるようにする方法を紹介した。
ついでに pre_get_posts
アクションフックの使い方も少し紹介した。
どちらかというと pre_get_posts
のほうを特に啓蒙していきたい思いがあるが……
ともあれ query_posts()
は滅ぶべきであると考える次第である。
ワードプレスのフォーラでご回答いただいたものです。
なぜか返信がうまくいかないのでこちらに記載させてください。
ありがとうございます。
FFFTP上の情報ですが
/htdocs/wp/wp-content/themes/my-corpolate
WEB例
http://sample.lv9.org/wp/blog?i=1
my-corpolateの中にすべてのphp(htmlファイルの事)があります。
・
フルスクラッチで作っているので本当はindex.php以外はhtmlフォルダ以外に入れたいのですが、
その場合は
'html/header.php' ) ); ?>
とすればよいという事ですかね?
仕様で決まっているルートディレクトリとは
FFFTP上の情報
/htdocs/wp/wp-content/themes/my-corpolate/index.php
ですとどこに当たるのでしょうか?
オリジナルのテーマのindex.phpがある場所ではないのでしょうか?
……どちらさま?
俺は記憶の糸を辿った。
ここの人かな?
せめて名前くらい名乗ってね。
あと、WordPressでサイトを運営してるならわかるだろうけど、このコメント欄ってコードを載せるのに向いてないんだよね。
とりあえず
< >
はエスケープ< >
しようね。WordPressのテーマってのは wp-content/themes/ にあるディレクトリのことで、そのパスでいうと wp-content/themes/my-corpolate/ がそのテーマのルートディレクトリになるね。
そこに置いた index.php が、WordPressにアクセスしたときに呼び出されるファイルになるね。
これはそのテーマを使う限り変えられない仕様になるね。
index.php からインクルードされるPHPはどこにあってもいいんだけど、
get_header()
とかは index.php のあるディレクトリからPHPファイルを呼び出すようになってるから、サブディレクトリから呼び出したい場合はget_template_part()
とかを使うことになるね。ただ、やっぱり header.php みたいなWordPressの標準のファイルはテーマのルートに置くべきじゃないかなぁ。