🔍

WordPressで複数キーワードの検索をする

2024/02/29に公開

実装の目的

記事タイトルに含まれた日付で範囲検索を行いたい。

WordPressの標準検索の仕様

ここでいう標準検索は、「s」パラメーターで検索するやつです。
これはAND検索になっているため、OR検索に書き換えて複数キーワードが入れられるようにする必要があります。

標準検索のSQL文は「posts_search」で取得できます。

functions.php
function custom_or_search($search, $wp_query) {
 var_dumo($search);
}
add_filter('posts_search', 'custom_or_search',10,2);

// 参考値
// 「%」は実際には「{66394edac5771119d265a828e35f8022dc73569aa1a42ac077362727087813bb}」のようなランダムな数列が入る。
/**
 * AND ((
 * (wp_posts.post_title LIKE '%2023年1月 2023年2月 2023年3月 2022年4月 2022年5月 2022年6月 2022年7月 2022年8月 2022年9月 2022年10月%') 
 * OR (wp_posts.post_excerpt LIKE '%2023年1月 2023年2月 2023年3月 2022年4月 2022年5月 2022年6月 2022年7月 2022年8月 2022年9月 2022年10月%') 
 * OR (wp_posts.post_content LIKE '%2023年1月 2023年2月 2023年3月 2022年4月 2022年5月 2022年6月 2022年7月 2022年8月 2022年9月 2022年10月%')))  
 * AND (wp_posts.post_password = '') 
 * /

ということで、この「$search」を書き換えちゃおう。

検索ワードを用意する

今回必要なのは、「2023年1月 2023年2月 2023年3月 2022年4月 2022年5月 2022年6月 2022年7月 2022年8月 2022年9月 2022年10月」のように年を空白で区切った検索ワード。
投稿タイトルもこの形式で設定をしています。

年度じゃなければ、「?s=2022」とか単一キーワードで検索ができるんだけどね。

functions.php
// URLのbacknumber-yearパラメーターを取得し、検索ワードを作成する。
// ※パブリッククエリ内にあるパラメーターを使用するとページネーションを妨害されます。
// 【参考】https://kinsta.com/jp/blog/wordpress-permalinks/

function backnumber_year_filter() {
  if(isset($_GET['backnumber-year']) && $_GET['backnumber-year'] != null) {
    $year = intval($_GET['backnumber-year']);
    $result = array();
    // パラメーターの年度の配列を作成する
    for ($i = 1; $i < 13; $i++) {
      if ($i > 0 && $i < 4) {
        $next_year = $year + 1;
        $date = new DateTime("{$next_year}-{$i}-01");
      } else {
        $date = new DateTime("{$year}-{$i}-01");
      }
        array_push($result, $date->format('Y年n月'));
    }

    $result = implode(' ', $result);// 配列を半角空白で繋いで文字列にする
    return $result;

    // $resultの参考値
    // > 2023年1月 2023年2月 2023年3月 2022年4月 2022年5月 2022年6月 2022年7月 2022年8月 2022年9月 2022年10月
  } else {
    // パラメーターがなければ空文字を返す
    return "";
  }
}

SQLクエリを書き換える

「1LIKE 1単語」になるよう、ループを回します。

functions.php
// キーワード検索をOR検索に変更
function custom_or_search($search, $wp_query) {
  global $wpdb;

// 対象のページ以外では$searchをそのまま返す
  if(parse_url($_SERVER["REQUEST_URI"])['path'] !== "/wp-json/wp/custom/posts" && !isset($_GET['backnumber-year'])) {
    return $search;
  }
  
  $search_words =  explode(" ",$wp_query->query_vars['search_terms'] ? $wp_query->query_vars['search_terms'][0] : '' );
  if( count($search_words) > 0 ) {  
    $where_clauses = array();
    foreach ($search_words as $keyword) {
      $search_word = $wpdb->escape("{$keyword}%");
      $where_clauses[] = "{$wpdb->posts}.post_title LIKE '{$search_word}'";
    }
    $where = implode(' OR ', $where_clauses);
    $query = "AND(" . $where . ") AND (wp_posts.post_password = '')";
    return $query;

    // $queryの参考値
    // 「%」はワイルドカードで、今回は前方一致検索をしている
    /** 
     * AND
     * (wp_posts.post_title LIKE '2023年1月%' 
     * OR wp_posts.post_title LIKE '2023年2月%' 
     * OR wp_posts.post_title LIKE '2023年3月%' 
     * OR wp_posts.post_title LIKE '2022年4月%' 
     * OR wp_posts.post_title LIKE '2022年5月%' 
     * OR wp_posts.post_title LIKE '2022年6月%' 
     * OR wp_posts.post_title LIKE '2022年7月%' 
     * OR wp_posts.post_title LIKE '2022年8月%' 
     * OR wp_posts.post_title LIKE '2022年9月%' 
     * OR wp_posts.post_title LIKE '2022年10月%' 
     * OR wp_posts.post_title LIKE '2022年11月%' 
     * OR wp_posts.post_title LIKE '2022年12月%') 
     * AND (wp_posts.post_password = '')
     */
  }
}
add_filter('posts_search', 'custom_or_search',10,2);

検索を実行する

わざわざREST APIで作ったんだけど、テンプレートファイルでも書けるはず。

functions.php
function get_backnumber($request)
{
    $args = array(
      'post_type' => 'himawari-tsushin',
      's' => backnumber_year_filter(),
      'posts_per_page' => 9,
      'order' =>'DESC',
      'orderby' => 'title',
      'paged' =>  $_GET['backnumber-paged'],
    );
    $response = new WP_Query($args);

    return rest_ensure_response($response);
}

/**
* This function is where we register our routes for our example endpoint.
* エンドポイントの設定
*/
function register_api_routes()
{
  register_rest_route('wp/custom', '/posts', array(
    'methods'  => WP_REST_Server::READABLE,
    'callback' => 'get_backnumber',
  ));
}

add_action('rest_api_init', 'register_api_routes');

Discussion