🔍
WordPressで複数キーワードの検索をする
実装の目的
記事タイトルに含まれた日付で範囲検索を行いたい。
WordPressの標準検索の仕様
ここでいう標準検索は、「s」パラメーターで検索するやつです。
これはAND検索になっているため、OR検索に書き換えて複数キーワードが入れられるようにする必要があります。
標準検索のSQL文は「posts_search」で取得できます。
functions.php
function custom_or_search($search, $wp_query) {
var_dump($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