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() は滅ぶべきであると考える次第である。