WordPressとajax (1)

WordPressとajax (1)
Image by Phil Riley from Pixabay

2021.10.11(更新日:2021.10.20)

非同期で条件に合わせて値を変化させるなんて場合は「Ajax」を使う。
Wordpressにはそれ様の関数なんかもあるので、やり方さえ分かれば問題ない・・・はずだったが
久々に使うとスッカリ脳内消失なので、サンプルを作りまとめてみる。
オレオレ式の説明なので正式な表現は多分してないのはご了承。

この記事は1年以上経過しています。内容的に古い場合があります。

サンプルの仕様

項目をチェックボックスにてフォーム内表示させ、選択するごとに記事の合計が表示されると言うもの。
submitする前にどれくらいの件数があるか確認出来るのでユーザービリティ向上には一役買える。
ただしサンプルなので簡潔にしたく、投稿とカテゴリーを使用して構成する。
これを応用し「複数のタクソノミーを持ちいて選択条件に一致した数を表示する」などが本来。
(なので「これならajax使う必要ないじゃん」とは言わないことを条件に読み進めて欲しい)

まずはテンプレート部分から。

<?php
/* テンプレート内にでも */
$lists = '';
$args = array(
	'orderby' => 'menu_order',
	'order' => 'ASC',
	'hide_empty' => 0
);
$categories = get_categories( $args );
foreach( $categories as $value ){
	if( $value->parent === 0 ){
		$lists .= '<label>';
	}
}
?>
<form method="get" id="searchform">
	<div><?php echo $lists; ?></div>
	<div>該当件数:<span id="search-result-count"></span>件</div>
	<div><input type="submit" value="検索"></div>
</form>

get_categoriesで取得したオブジェクトのparent値が0のものだけ表示することにより、最上位のタームだけ表示。
チェックボックスの値にはタームの投稿数を入れる。
ajaxで取得した値はsearch-result-count内に件数を表示させる。

手っ取り早くjQueryを使う

調べた結果、JavaScriptで非同期に通信を行うには以下の方法がある。

  • XMLHttpRequest・・・JavaScriptの組み込みオブジェクト
  • jQuery.ajax・・・jQueryメソッド(ただしバージョンによって使えるものが変わる)
  • axios・・・ajax通信専用のライブラリ(らしい。使ったことなし)

今回はWordpressにて使用するので実績のあるjQueryを使用する。解説サイトもいっぱいあるので、手始めには一番無難かなと思う。また何とかie11もカバーしてくれている様子なので、未だにie11・・・と言うお客様もフォロー出来る。(かもしれない)

アクションフックで下準備

JSで何らかの値を投げてPHPで取得し、PHPで処理した値をまたJSに返す。これがざっくりした流れ。
あと重要なのが言語を取り持つjson。この型式で言語の違いをカバーする。
今回Worepressを使うので、作法に従いアクションフックを用いて行う。
兎にも角にも最初にjQueryをテーマで読み込ませる。

/* functions.phpなど */
add_action('wp_enqueue_scripts', 'my_load_scripts');
function my_load_scripts() {
	wp_enqueue_script( 'jquery' );
	wp_enqueue_script( 'jquery-migrate' );
}

上記はWordpressに梱包されているものをそのまま読み込んだ例。Wordpress ver5.8.1 で

  • jQuery ver3.6
  • jQuery Migrate ver3.3.2

と意外と最新のものだった。(2021.10) このバージョンなら流用してもいいかな。

nonceを加えてJSに渡す

なんてことなくDBのデータを取ってこれるajax。逆に言えば悪用されたらとっても怖い。
そこで「nonce」と言う一時的な値を生成、確認要素とし値を比較することで「処理するか中断」を判断させる。

/* functions.phpなど */
add_action( 'wp_enqueue_scripts', 'my_enqueue_scripts' );

function my_enqueue_scripts() {
	$handle = 'sample_handle'; // 1. JSファイルにhandle名を付ける
	$action = 'sample-ajax-action'; // 2. Ajaxのアクション名
	$object_name = 'sampleAjax'; // 3. JSで扱う変数名

	// 4. ajaxの指示を書いたjsファイルの読み込み。外部ファイル
	wp_enqueue_script( $handle, get_template_directory_uri().'/js/script-ajax.js', array( 'jquery' ) );

	// 5. JS側へAjaxで使う各パラメータを渡す
	wp_localize_script( 
		$handle, 
		$object_name, 
		array(
			'url' => admin_url( 'admin-ajax.php' ),
			'action' => $action,
			'nonce' => wp_create_nonce( $action ),
		) 
	);
	wp_enqueue_script( $handle );
}

この部分整理しておかないと結構ゴチャゴチャしてくるけど

  1.  スクリプトの識別名を決める。ユニークな値
  2.  JSとPHPで共有するアクション名。ユニークな値
  3.  wp_localize_scriptで生成された値を入れとく変数名。ユニークな値(アンダースコアまたはキャメルケースを使用)
  4.  読み込み場所やファイル名は適宜変更
  5.  wp_localize_script を使いJSで使うパラメータを生成。array部分の値をscriptタグで表示

これでwp_localize_scriptによってJSで使う要素をscritpタグにて表示(head内)してくれるので、JSで拾える。
$objct_nameで指定した「sampleAjax」にurlやaction、nonceの値もあるのが確認出来ると思う。

/* 公開側のhead内に出されたもの */
<script id='sample_handle-js-extra'>
var sampleAjax = {"url":"http:\/\/xxx.com\/wordpress\/wp-admin\/admin-ajax.php","action":"sample-ajax-action","nonce":"f15adbcb77"};
</script>

JSファイルの制作

/* script-ajax.js */
(function($) {
	$(function (){

		$("#searchform").change(function() {
			seachMatchCount();
		});

		function seachMatchCount(){
			$.ajax({
				type: 'POST',
				url: sampleAjax.url, // A
				dataType: 'json',
				data: {
					action : sampleAjax.action, // A
					nonce: sampleAjax.nonce, // A
					terms : term_id // B
				}
			}).done( function( data ) {
				// 成功時。出力する部分(処理)
				$('#search-result-count').html(data);
			}).fail( function( XMLHttpRequest, textStatus, error ){
				// 通信失敗時のコールバック処理
				console.log( 'XMLHttpRequest : ' + XMLHttpRequest.status );
				console.log( 'textStatus     : ' + textStatus );
				console.log( 'errorThrown    : ' + error.message );
			}).always(function (data) {
				// 常に実行する処理
			});
		}

	});
})(jQuery);

ajaxを発火させる処理

submitされてからでは意味ない。checkboxをチェックに対して反応して欲しいので「change」で対応。
(今回はチェックボックスしかないのでフォームのパーツが変更されたらとしている)

ajax処理

「seachMatchCount」の部分。ajaxの説明をしているサイトは多々あるけど「done / fail / always」の書き方が良いらしい。
(jQueryのバージョン等々にもよるかも。少なくとも3.Xはこれで書く)

A」の部分は、前の章でアクションフック値を記述。(my_enqueue_scriptsの$object_name)
B」はフォームによってPOSTされる値をキー付きで記述。(フォームの input checkbox のname値)
「done」部分は成功時。戻り値を書き込み処理。

失敗時にはコンソールにてエラー表示するようにした。alwaysは今回必要ないと思えるが、ajaxの記述型式を説明するために入れてある。

JSON

説明が結構面倒なのでパス。実際「やんわり」としか分かってないので以下参照。

アクションフックで処理コード

JSの方で「チェックボックスをチェックしたら件数を返せ」と記述した。
その際チェックボックスの値がajaxでPOSTにて送信される。これを受け取ってPHPで処理して返すと言うのがこれ。
POSTされた値は数字が入った単なる配列。で、それらの合計を返却。

/* functionsとかに */
add_action( 'wp_ajax_sample-ajax-action', 'get_match_count' );
add_action( 'wp_ajax_nopriv_sample-ajax-action', 'get_match_count' );

function get_match_count(){
	$count = 0;
	if( $_SERVER['REQUEST_METHOD'] === 'POST' && check_ajax_referer( 'sample-ajax-action', 'nonce' ) ) {
		terms = filter_input( INPUT_POST, 'terms', FILTER_DEFAULT, FILTER_REQUIRE_ARRAY );
		if( isset( $terms ) ){
			foreach( $terms as $value ) $count = $count + (int)$value;
		}
	}
	header("Content-type: application/json; charset=UTF-8");
	echo json_encode( $count );
	wp_die();
}

なんだか色々書いてあるが、パターン化し使い回すのが吉。

/* PHP */
add_action( 'wp_ajax_(アクション名)', '処理関数名' );
add_action( 'wp_ajax_nopriv_(アクション名)', '処理関数名' );
function 処理関数名(){
	if( $_SERVER['REQUEST_METHOD'] === 'POST' && check_ajax_referer( 'アクション名', 'nonce' ) ) {
		// 処理を記述
	}
	header("Content-type: application/json; charset=UTF-8");
	echo json_encode( JSに返す変数 );
	wp_die();
}

まとめ

必須のnonceを入れるところで、アクション名とJSの変数名の位置づけを理解する。
今回のサンプルは「配列をPOSTし、単一の値を返却」するものなので、JSON部分を意識しないでも出来た。
次回は「配列でPOST、配列で返却」をやる予定。