WordPress
カスタマイズ事例

WORDPRESS CUSTOMIZATION

Ajaxでページ遷移なしの絞り込み検索をつくる テーマ販売あり

フォームでメタタグやカテゴリなどの複合条件を選択して絞り込み検索する」では、フォームを使ったサイト内絞り込み検索の事例を紹介しました。この事例ではページ遷移して検索結果が表示される仕様でした。

今回はAjaxを使って、検索時にページ遷移しない絞り込み検索を作ってみます。
WordPressはデフォルトでAjax処理をするファイル(/wp-admin/admin-ajax.php)を持っているので、そのファイルを使います。
 
例として、カスタム投稿タイプで管理する「仕事情報(jobs)」を、カスタムタクソノミーで管理する「スキル(jobs_skill)」カスタムフィールド(アドバンストカスタムフィールド)で管理する「エリア(jobs_area)」を条件にして絞り込む、絞り込み検索をつくることとします。

作る(あるいは編集する)ファイルは4つ。

  • HTMLテンプレート: archive-jobs.php
  • Ajaxで呼び出されるテンプレート: page-templates/ajax-search.php
  • おなじみ、テーマの機能を管理するプログラム用ファイル: functions.php
  • フォームの内容をAjaxでサーバーに送るjavascript: search.js

それではひとつひとつ見ていきましょう。
 
まずテンプレートファイルの記述(必要最小限の記述のみ)からいきましょうかね。
archive-jobs.php

<?php
// カスタム投稿タイプ
$post_type = 'jobs';

get_header(); ?>

<?php
$jobs_area_obj = get_field_object('jobs_area');
$jobs_area_choices = $jobs_area_obj['choices'];
$jobs_area_choices_keys = array();
foreach ($jobs_area_choices as $key => $value) {
  $jobs_area_choices_keys[] = $key;
}
$jobs_area_labels = $jobs_area_obj['value'];
$jobs_area = $_GET['jobs_area'] ?? null;
if ($jobs_area == 'all' || $jobs_area == '') {
  $jobs_area = $jobs_area_choices_keys;
}

$jobs_skills_selected = array();
$jobs_skill = array();
$jobs_skill_terms = get_terms('jobs_skill', array('orderby' => 'term_order', 'order' => 'ASC'));
$jobs_skills = $_GET['jobs_skill'] ?? null;
if ($jobs_skills) {
  foreach ((array)$jobs_skills as $value) {
    $jobs_skills_selected[] = htmlspecialchars($value);
    $jobs_skill[] = htmlspecialchars($value);
  }
} else {
  foreach ($jobs_skill_terms as $jobs_skill_term) {
    $jobs_skill[] = $jobs_skill_term->slug;
  }
}
?>
<form method="get" action="<?php echo home_url('/'); ?>jobs/">
  <div>
    <h3>エリアから絞り込む</h3>
    <div>
      <select name="jobs_area">
        <option value="all">すべて</option>
        <?php
        foreach ($jobs_area_choices as $key => $value) {
          echo '<option value="' . $key . '" ';
          if ($jobs_area) {
            if ($key == $jobs_area) {
              echo 'selected="selected"';
            }
          }
          echo '>' . $value . '</option>';
        }
        ?>
      </select>
    </div>
  </div>
  <div>
    <h3>スキルから絞り込む</h3>
    <div>
      <?php foreach ($jobs_skill_terms as $jobs_skill_term) {
        echo '<label><input type="checkbox" name="jobs_skill[]" value="' . $jobs_skill_term->slug . '" ';
        if ($jobs_skills_selected) { //選択した項目はページ遷移後 checked
          foreach ($jobs_skills_selected as $value) {
            if ($value == $jobs_skill_term->slug) {
              echo 'checked';
            }
          }
        }
        echo '> ' . $jobs_skill_term->name . '</label>';
      } ?>
    </div>
  </div>
</form>

<div class="search-results-container">
  <div id="search-result"></div>
</div>

<?php get_footer(); ?>

後述する search.js で、最後の

<div id="search-result"></div>

の中にAjaxで取得したHTMLを挿入します。だからテンプレート上は空でOK。
そして、そのAjaxで取得するHTML部のテンプレートは page-templates/ajax-search.php に記述します。
page-templates/ajax-search.php

$post_type = 'jobs';
$jobs_area_obj = get_field_object('jobs_area');
$jobs_area_choices = $jobs_area_obj['choices'];
$jobs_area_choices_keys = array();
foreach ($jobs_area_choices as $key => $value) {
  $jobs_area_choices_keys[] = $key;
}
$jobs_area_labels = $jobs_area_obj['value'];
$jobs_area = $_GET['jobs_area'] ?? null;
if ($jobs_area == 'all' || $jobs_area == '') {
  $jobs_area = $jobs_area_choices_keys;
}

$jobs_skills_selected = array();
$jobs_skill = array();
$jobs_skill_terms = get_terms('jobs_skill', array('orderby' => 'term_order', 'order' => 'ASC'));
$jobs_skills = $_GET['jobs_skill'] ?? null;
if ($jobs_skills) {
  foreach ((array)$jobs_skills as $value) {
    $jobs_skills_selected[] = htmlspecialchars($value);
    $jobs_skill[] = htmlspecialchars($value);
  }
} else {
  foreach ($jobs_skill_terms as $jobs_skill_term) {
    $jobs_skill[] = $jobs_skill_term->slug;
  }
}

$the_query = new WP_Query(array(
  'post_status' => 'publish',
  'post_type' => $post_type,
  'tax_query' => array(
    'relation' => 'AND',
    array(
      'taxonomy' => 'jobs_skill',
      'field' => 'slug',
      'terms' => $jobs_skill,
    )
  ),
  'meta_query' => array(
    'relation' => 'AND',
    array(
      'key' => 'jobs_area',
      'value' => $jobs_area,
      'compare' => 'IN'
    )
  ),
  'posts_per_page' => -1,
  'orderby' => 'date',
  'order' => 'DESC',
));

if ($the_query->have_posts()) {
  $html = '';
  while ($the_query->have_posts()) {
    $the_query->the_post();
    $jobs_title = get_the_title();
    $jobs_area= get_field('jobs_area');

    $html_result = '<ul>';
    $jobs_skill_cats = wp_get_object_terms($post->ID, 'jobs_skill');
    foreach ($jobs_skill_cats as $jobs_skill_cat) {
      $jobs_skill_cat_name = $jobs_skill_cat->name;
      $html_result .= '<li>' . $jobs_skill_cat_name . '</li>';
    }
    $html_result .= '</ul>';

    $html .= '<div>';
    $html .= '<h2>' . $jobs_title . '</h2>';
    $html .= '<table>
        <tr><th>地域</th><td>' . $jobs_area . '</td></tr>
        <tr><th>スキル</th><td>' . $html_result . '</td></tr>
      </table>';
    $html .= '</div><hr>';
  }
  echo $html;
} else {
  echo 'ありません。';
} ?>

 
 
次は、テーマの functions.php に以下を追記しましょう。

/* フォームの内容を取得してAjaxに送信するプログラムが記述されたJavascriptをheadタグにキュー */
add_action('wp_enqueue_scripts', function () {
  $handle = 'search';
  $file = get_template_directory_uri() . '/assets/js/' . $handle . '.js';
  wp_register_script($handle, $file, array('jquery'), '3.6.0', true);

  $localize = [
    'ajax_url' => admin_url('admin-ajax.php'),
    'action' => 'view_search_results',
    'nonce' => wp_create_nonce('view_search_results')
  ];
  wp_localize_script($handle, 'localize', $localize);
  wp_enqueue_script($handle);
});


/* Ajaxから取得したnonceを認証し、認証通過したらテンプレートを出力する */
function view_search_results()
{
  $nonce = $_REQUEST['nonce'];
  if (wp_verify_nonce($nonce, 'view_search_results')) {
    get_template_part('page-templates/ajax-search');
  }
  die();
}

add_action('wp_ajax_view_search_results', 'view_search_results'); //第一引数は wp_ajax_{ファンクション名} にする
add_action('wp_ajax_nopriv_view_search_results', 'view_search_results'); //第一引数は wp_ajax_nopriv_{ファンクション名} にする

nonceというのは一時的な専用パスワードみたいなもので、CSRF(クロスサイトリクエストフォージェリ)対策として使います。
このnonceをフォームの内容と一緒にJavascriptで受け取ってサーバーに送り、WordPressのPHPが認証をする流れです。
 
そのjavascript: search.js が最後のファイルです。
name=’jobs_area’のプルダウンと name=’jobs_skill[]’のチェックボックスがあり、ユーザーが選択肢を触ってそれらの状態が変化したらAjaxを走らせます。

$("select[name='jobs_area'], input[name='jobs_skill[]']").change(function () {
  dispLoading();

  let jobs_skill = [];
  $("[name='jobs_skill[]']:checked").each(function () {
    jobs_skill.push(this.value);
  });
  $.ajax({
    type: 'GET',
    url: localize.ajax_url,
    data: {
      'action': 'view_search_results',
      'jobs_area': $('[name=jobs_area]').val(),
      'jobs_skill': jobs_skill,
      'nonce': localize.nonce
    },
    success: function (response) {
      $('#search-result').html(response);
    }
  })
    // 処理終了時
    .always(function (response) {
      removeLoading();
    });

  return false;
});

/* ------------------------------
 Loading
 ------------------------------ */
function dispLoading(msg) {
  if (msg == undefined) {
    msg = "";
  }
  var dispMsg = "<div class='loadingMsg'>" + msg + "</div>";
  if ($("#loading").length == 0) {
    $("body").append("<div id='loading'>" + dispMsg + "</div>");
  }
}

function removeLoading() {
  $("#loading").fadeOut('fast').queue(function() {
    $("#loading").remove();
  })
}

ローディング中の画面も出すようにしてます。
そのCSSも一応載せておきます。(記述内のloading.gifはご自身でご用意ください。くるくる回るやつですね)

/* loading */
#loading {
    display: table;
    width: 100%;
    height: 100%;
    position: fixed;
    top: 0;
    left: 0;
    background-color: #fff;
    opacity: 0.8;
}

#loading .loadingMsg {
    display: table-cell;
    text-align: center;
    vertical-align: middle;
    padding-top: 140px;
    background: url("../../images/loading.gif") center center no-repeat;
}

 
いかがでしたでしょうか?
いつもよりかなり長めになりましたが、出来上がりはなかなか気持ちの良い動きを見せてくれるものになっていると思いますので、がんばって作ってみてください!
 
なお、この事例では検索結果を1ページに全件表示します。
全件ではなく分割して、「もっと見る」ボタンなどで次の検索結果を表示したいケースでは、以下の事例を使ってください。
絞り込み検索 &『もっと見る』ボタンを、Ajaxを使ってページ遷移なしで実現する
 
 
-----------
Ajaxがらみですと、検索ではなく、通常の記事一覧のページネーションをAjaxでおこなうこととかありますよね。
100ウェブではそれについて紹介した事例もありますので、興味がある方はこちらもどうぞ。
Ajaxでページネーションなしのターム(カテゴリ)別投稿記事一覧ローディングをつくる

【この事例を解決するためのWordPressテーマを購入できます】

現在あなたが利用されているWordPressテーマを活かしたまま、このページの事例を解決できるWordPressテーマを、子テーマとして購入できます。
この子テーマを有効化するだけで、現在お使いのデザインテーマにこの事例解決の機能が自動付与されます。
デザインが入っていないため、一般より格段に低い価格(事例の難易度により200円~1.3万円)です。
※ テーマは買い切りです
※ 自由に改変してお使いいただいて構いません
※ すでに子テーマでサイト運用されている方は、当該子テーマをマージしてください

【100ウェブ新着情報メルマガ】

WordPressカスタマイズ事例やウェブ制作ノウハウの新着情報、お役立ち情報を
リアルタイムにメルマガ配信!