3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

WordPressでインクリメンタルサーチを自前で実装する

Last updated at Posted at 2019-02-01

はじめに

『WordPress 検索』でググるとだいたい search.phpsearchform.php を使った同期的な検索の実装方法がヒットします。
非同期でインクリメンタルサーチを行う方法がパッと出てこなかったのでこの記事にまとめます。他により良い方法があればぜひ教えてください。

要件

  • カスタムポストを検索
  • 表示項目はサムネイルと名前
  • インクリメンタルサーチ
  • NIKE,ないき,ナイキのどれでもヒットするようにする

バージョン

WordPress 4.9.8
Advanced Custom Fields 5.7.10
Custom Post Type UI 1.6.1
WP REST API 2.0-beta15

完成形

こんなイメージになります。
incrementalsearch.gif

実装の流れ

  • 「ブランド」というカスタムポストを作る
  • カスタムポスト に検索用キーワードというカスタムフィールド を付与する
  • 表示項目の「サムネイル」はカスタムポスト のアイキャッチ画像で対応し「名前」はカスタムポスト のタイトルで対応する。
  • WP REST APIを使って検索結果はJSON形式で返すようにする
  • そのレスポンスを元にjQueryでゴリゴリ描画する

具体的な手順

必要なプラグインをインストールする

  • WP REST API
  • Advanced Custom Fields
  • Custom Post Type UI

スクリーンショット 2019-01-30 9.53.23.png

スクリーンショット 2019-01-30 14.23.27.png

スクリーンショット 2019-01-30 14.23.54.png

固定ページ「検索ページ」を作成する

スクリーンショット 2019-01-30 9.52.15.png

カスタムポスト 「ブランド」を作成する

メニューから「投稿タイプの追加と編集」を選択
スクリーンショット 2019-01-30 14.26.26.png

下記のように入力する

スクリーンショット 2019-01-30 20.08.43.png

カスタムフィールドを作る

検索用キーワードのカスタムフィールド を作る

スクリーンショット 2019-01-30 14.51.07.png

このカスタムフィールド グループをカスタムポスト 「ブランド」に紐づける
スクリーンショット 2019-01-30 14.51.20.png

カスタムポストを作成する

以下を入力してください。
- タイトル
- アイキャッチ画像
- 検索用キーワード

検索用キーワードにはヒットさせたい文字列を続けて入力してください。
NIKEでもないきでもナイキでもヒットさせたいので
「NIKEないきナイキ」としました。

この実装方法は一般的にはアンチパターンだとは思いますがWordPressだと他に実装方法が思いつきませんでした。

スクリーンショット 2019-01-30 14.53.15.png

同様に他にもデータを作成してください。今回はNIKEの他にadidasとUnderArmourを作りました。

functions.phpを編集する

以下をfunctions.phpに追加してください。


// rest_{post_type}_query
add_filter( 'rest_brand_query', 'my_rest_brand_query', 10, 2 ); 

function my_rest_brand_query( $args, $request ) {
  // keywordというところはカスタムフィールド 「検索用フィールド」のslug名
  if ( isset( $request['keyword'] ) ) {
    $args['meta_query'] = array(
      array(
      "key" => "keyword",
      "value" => $request['keyword'],
      "compare" => "LIKE" // 部分一致なので"LIKE"とした。完全一致だったら"="にする
      )
    );
   return $args;
  }
}

固定ページの検索ページに対応するテンプレートを作成する

page-search.php を作成する
page-{固定ページのslug名}.php という命名規則。よくわからない人はテンプレートの優先順位を確認してください。

page-search.php
<?php
get_header(); ?>

<div class="wrap">
    <div id="primary" class="content-area">
        <main id="main" class="site-main" role="main">


        </main><!-- #main -->
    </div><!-- #primary -->
</div><!-- .wrap -->
<?php get_footer();

検索フォームと検索結果を表示する領域を作る

page-search.php
<?php
get_header(); ?>

<div class="wrap">
    <div id="primary" class="content-area">
        <main id="main" class="site-main" role="main">
            <input id="search-form" type="search">

            <ul id="result-lists"></ul>

        </main><!-- #main -->
    </div><!-- #primary -->
</div><!-- .wrap -->
<?php get_footer();


Ajax通信でカスタムポスト を検索する処理を書く

page-search.php
.
.
省略
<script>
    jQuery(function($){
        $("#search-form").on("keyup", function(event){
            // 入力値が変更されたらとりあえず結果を消す
            $("#result-lists").empty()
            // 入力値が空だったら処理終了
            if (event.target.value == "") { return }
            $.ajax({
              // localhost:8000の部分は各自置き換えてください。
              // _embedパラメータはアイキャッチ画像をレスポンスに含めるのに必要です。
              // keywordパラメータに検索フォームの入力値を設定します。
               url:"http://localhost:8000/wp-json/wp/v2/brand/?_embed&keyword=" + event.target.value,
               type:'GET'
            })
            // Ajaxリクエストが成功した時発動
            .done( (data) => {
                // 検索結果を描画する
                refreshHTML(data)
            })
            // Ajaxリクエストが失敗した時発動
            .fail( (data) => {
                // エラー処理
            })
            // Ajaxリクエストが成功・失敗どちらでも発動
            .always( (data) => {

            });
        })
                // 検索結果描画用の関数
        function refreshHTML(data) {
            if (data.length > 0) {
                $.each(data, function(index, val) {
                    // タイトルは val.title.renderedで取得できる
                    // 画像のURLは val._embedded["wp:featuredmedia"][0].source_urlで取得できる
                    $("#result-lists").append("<li><img src=" + val._embedded["wp:featuredmedia"][0].source_url + " style='width: 50px; height: 50px; vertical-align: middle;'>" + val.title.rendered + "</li>")
                })
            }
        }
    })
</script>

とりあえずこれでインクリメンタルサーチができるはずです。
ただこれだとnikeで検索しようとした時に
nを打ったタイミングで通信が行われ
iを打ったタイミングで通信が行われ
kを打ったタイミングで通信が行われ
eを打ったタイミングで通信が行われ
という動きになります。

素早くniと打ったタイミングでは、nを打ったタイミングで行われた通信を中断してやるのが親切です。
つまり直前のリクエストを中断する処理があったほうが親切ということです。
それを次に書いて終わります。

直前のリクエストを中断する

page-search.php
.
.
省略
<script>
    jQuery(function($){
        // 追加 ajax中止用
        var jqxhr;
        $("#search-form").on("keyup", function(event){
            // 入力値が変更されたらとりあえず結果を消す
            $("#result-lists").empty()
            // 追加 直前のajax通信を中断する
            if (jqxhr) {
                jqxhr.abort();
            }
            // 入力値が空だったら処理終了
            if (event.target.value == "") { return }
            // 追加Ajax通信
            jqxhr = $.ajax({
              // localhost:8000の部分は各自置き換えてください。
              // _embedパラメータはアイキャッチ画像をレスポンスに含めるのに必要です。
              // keywordパラメータに検索フォームの入力値を設定します。
               url:"http://localhost:8000/wp-json/wp/v2/brand/?_embed&keyword=" + event.target.value,
               type:'GET'
            })
            // Ajaxリクエストが成功した時発動
            .done( (data) => {
                // 検索結果を描画する
                refreshHTML(data)
            })
            // Ajaxリクエストが失敗した時発動
            .fail( (data) => {
                // ajax通信を中断した時もfailの方にくるのでエラー処理とは分けて考える
                if (data.statusText == "abort") { return }
                // エラー処理
            })
            // Ajaxリクエストが成功・失敗どちらでも発動
            .always( (data) => {

            });
        })
                // 検索結果描画用の関数
        function refreshHTML(data) {
            if (data.length > 0) {
                $.each(data, function(index, val) {
                    // タイトルは val.title.renderedで取得できる
                    // 画像のURLは val._embedded["wp:featuredmedia"][0].source_urlで取得できる
                    $("#result-lists").append("<li><img src=" + val._embedded["wp:featuredmedia"][0].source_url + " style='width: 50px; height: 50px; vertical-align: middle;'>" + val.title.rendered + "</li>")
                })
            }
        }
    })
</script>

これでこのような動きが実現できると思います。
再掲

incrementalsearch.gif

3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?