wordpressでajaxを使ってみる
最近では当たり前のように使われていて、空気のような存在になっているajax。その所為かネット上では古い情報と新しい情報とが入り乱れており、初心者にとってはなかなか情報を得るのが難しい状況下もしれないですね。
すでに習熟している人にとっては当たり前すぎる技術であり、その為ライブラリを用いたajaxの解説(主にはjQuey)の例がほとんどで、javascriptネイティブで書かれることは少ないです
そんな中、ajaxの基本的な使い方をwordpressを通じて解説してみたいと思いマス。
ajaxについて
もうご存知かと思うが、一応説明しておこうと思います。
非同期通信である
ajaxは非同期でクライアントとサーバが通信する技術です。
普通何らかの処理がある場合、その処理が終わるまで他のプロセス(処理)は待っている状態になります。
たとえばHTMLの解析が行われているときに、cssのlinkが出てきたら、styleシートファイルの読み込み完了までHTMLの解析プロセスは待たないといけません。
他プロセスを停止させてしまうので、このような処理はブロッキング処理と呼ばれています。同期処理とはこのブロッキング処理とほとんど同じ意味です。
では非同期処理はというと、ノンブロッキング処理になるのか?
そのとおり。非同期処理は他のプロセスを停止させません。人間の感覚的には多数のプロセスが同時進行で進むような感じです(厳密な意味では同時進行ではないが)。
処理が重くなって遅延したり、止まり過ぎてしまう可能性を小さくできるということです。
ページ遷移なしで更新ができる
サイトの情報を更新する場合にはページ遷移をしなくては更新できませんでしたが、ajaxを使うと、ページ遷移なしに情報の更新が行えます。
ユーザーがフォームに打ち込んだ情報をサーバーに送り、適切な情報を返して画面を更新する…といったようなことがページ遷移なしでおこなえるのです。
もちろん画面全体でも一部分だけでも、一要素だけでも非同期通信による更新が可能です。
wordpressでajax
wordpressでajaxを扱うとき、知っておいた方がいい前提条件がいくつかあります。
1.wordpressが用意してくれているajax経路
wordpressはajaxの経路を準備してくれている。wp-admin/admin-ajax.phpにリクエストを送る方法です。この経路が一般的にwordpressでのajax経路として知られているかと思います。
このadmin-ajax.phpを経由するメリットは、ログイン状態、非ログイン状態でレスポンスを簡単に切り分けられるということです。
すなわち管理画面などでajaxを使う場合にはadmin-ajax.phpにリクエストを送る形でajaxを組んだ方が楽になると思います。
具体的にはfunctions.phpに書いた関数をアクションフィルターに登録して、ajaxのレスポンス関数とします。
2.独自ajax経路
wordpressが用意したajax経路でも大抵のことは行えると思いますが、admin-ajax.phpを経由せずに、独自のルートでajaxを組むこともできます。この方法ではレスポンスに使いたい処理はphpファイルにして、適切にデプロイすることになるかと思います。
この場合、リクエストはこのphpファイルに対して行われる形となります。
公開部分(ユーザが見るサイト部分)に対してajaxを行いたい場合は、独自ajax経路を使うのが一般的のようです。
どちらにするか
ajaxの扱いに慣れていないのなら、wordpressが用意してくれているajax経路を使うのがいいのではないかと思います。確かにこの方法は管理画面カスタマイズ用途とも言えなくはないですが、公開部分(ユーザが見るサイト部分)に対して使えない訳ではないです。
むしろfunctions.phpを使って手軽にajaxを組むことができますので、@MINOとしてはまずadmin-ajax.phpにリクエストを送る方法に慣れてから、独自ajaxに進んでもいいのではないかと思います。
jQueryを使うべきか?
ajaxはAsynchronous JavaScript + XMLの略なので、当然javascriptを使うことになりますが、ajaxの処理をどのように書くかを決めなくてはなりません。
一般的にはjQueryのajaxメソッドを使って書くのが一般的だと思います。他のサイトを探してもらっても、おそらくjavascript部分はjQueryを用いて書かれている例がほとんどだと思います。
しかし環境によっては他のフレームワークを使っていたりしてjQueryを使えない、もしくは使いたくない場合もあるかもしれません。そんな時はjQuery以外のajax用ライブラリを使う手もあります。
ただjQueryが大多数で使われている状況なので、それぞれのajax用ライブラリの情報は少ないと言わざるを得ません。
この記事を読んでいただいている方はおそらくまだajaxに慣れていないかと思うので、混乱してしまう恐れも無きにしも非ず。
また根本的な事を言えば、jQueryではラッピングがされ過ぎていて(だから楽なのだが)、ajaxを理解する目的には逆に仇となるかもしれません。
そこで今回に関しては(@MINOの)勉強の意味合いも強いので、ネイティブでの書き方とjQueryでの書き方を併記したいと思います。
ネイティブ書き方とjQueryを比較してもらうことでajaxの仕組みが理解しやすくなるかと思いますし、理解出来たなら、楽なjQueryを使うという手順でもいいのではないかと思います。
さらに習熟したら、こんな記事はポイーでご自分でjQueryライブラリを探すのもよし、自分専用ライブラリを作ってしまうのもアリだと思います。
GETとPOST
WebブラウザとWebサーバはHypertext Transfer Protocol(はいぱーてきすととらんふぁぷろとこる)という通信プロトコルで通信しています。
その通信に使われているメソッド(機能のようなもの)があり、それがGETとPOSTです。(他にもいくつかメソッドがあるがほとんど使われないので無視していい)
GETはクライアントから指定されたURIのリソース(ページとか画像とか)を取り出すメソッド。サーバからのレスポンスは通常GETになります。
POSTはクライアント(つまりwebブラウザ)からサーバに情報を送る際に使われるメソッドです。たとえばフォームに入力した情報を送る場合は通常POSTメソッドでサーバにリクエストすることになります。
「いや嘘を言うな、GETでもクライアントからサーバに情報を送れると聞いた」、という方がいるかもしれません。
確かに情報を送る事は可能です。URIにクエリとして情報を付加すればいいです。GETはURIを送るメソッドだから、クエリを使えば間接的に情報を送ることが可能です。
ただあまり長い情報を送る事は出来ず、基本的には制限があります。さらにクエリをURIにくっつける処理が意外と面倒。少ないクエリならいいのだが、何十個ともなると厄介になってきます。
これらの事を念頭にGETとPOSTを使い分ける必要があります。
wordpressでのajaxの実例
随分と前置きが長くなってしまったが、実際にajaxの処理を行ってみます。
functions.phpのレスポンス用関数の定義
function testAjax(){
echo "あいうえお";
die();
}
add_action( "wp_ajax_testAjax" , "testAjax" );
add_action( "wp_ajax_nopriv_testAjax" , "testAjax" );
まず、functions.phpのコード。レスポンス用の関数を定義します。この関数がブラウザに情報を返すという訳です。 ともかく簡単でわかりやすい例として、”あいうえお”を返すだけにします。
die()
コードでreturnを使っていない理由は、returnを使うとレスポンスに0が返ってしまう為。だからdie()で処理を終わらせています。 関数自体はほかに説明することが無いくらいシンプルです。
ajax用のアクションフックに登録
add_action( "wp_ajax_testAjax" , "testAjax" );
add_action( "wp_ajax_nopriv_testAjax" , "testAjax" );
この関数をアクションフックに登録する必要がある。
アクションフック処理をするadd_actionメソッドが二つありますが、一つ目はログイン状態での登録で、二つ目は非ログイン状態での登録です。
今回はログイン状態で切り分ける事をしていませんが、当然関数を二つ用意して、それぞれに別々な関数をフックさせれば、ログイン、非ログインでの処理の切り分けが非常に楽になる事がわかっていただけるかと思います。
add_actionメソッドの第二引数は関数名をしていするが、第一引数は少し変わっていて、アクション名に接頭語を付けなくてなりません。
ログイン状態の方は「wp_ajax_」、非ログイン状態は「wp_ajax_nopriv_」を付けます。関数名をくっつけるのが一番わかり良いかと思思います。
これでレスポンス用の関数の準備が整いました。
リクエスト用のjavascriptの用意 ネイティブでのコード例
今度はリクエスト側だ。さっき作ったtestAjaxに「情報よこせ」と言う為のjavascriptです。
ネイティブの説明を先にします。コードは短いがちょっと判りにくい部分もあるかと思いますが、以下がネイティブでのコード例です。
function ajax(){
var testRequest = new XMLHttpRequest();
testRequest.onreadystatechange = function (){
if( testRequest.readyState === 4 ){
if( ( 200 <= testRequest.status && testRequest.status < 300 ) ){
document.getElementById( "hoge" ).textContent = testRequest.response ;
}else{
console.log( "リクエスト失敗" );
}
}
};
testRequest.open( "POST" , "https://unskilled.site/wp-admin/admin-ajax.php" , true );
testRequest.setRequestHeader( "content-type", "application/x-www-form-urlencoded ; charset=UTF-8" );
testRequest.send( "action=testAjax" );
}
XMLHttpRequestオブジェクトの生成
var testRequest = new XMLHttpRequest();
まずXMLHttpRequestオブジェクトを生成しています。このオブジェクトを介してサーバと通信することになります。XMLHttpRequestオブジェクトはさまざまなプロパティやメソッドを持っていてそれらを使うことで、ajaxを実現させます。
onreadystatechangeイベント
testRequest.onreadystatechange
このイベントはreadyStateプロパティ(後述)が変化した場合に発火します。その為、このイベントにハンドラをアタッチすることでreadyStateプロパティの状態を逐一観察することができます。
readyStateプロパティ
testRequest.readyState
このプロパティで通信状態を取得できます。ajaxではさまざまな通信状態が存在するわけです。
状態は0~4数字で表され、意味は以下のようになっています。
番号 | 意味 |
---|---|
0 | リクエストが初期化されていない |
1 | サーバとの接続確立 |
2 | (サーバが)リクエストを受信した |
3 | (サーバの)リクエストの処理中 |
4 | レスポンスの準備完了 |
readyStateプロパティの値を監視しておけば、今がどういった状態なのかが判る訳です。事実上0~3が通信中で4が完了と考えてもいいかと思います。先ほども述べたとおりonreadystatechangeイベントをハンドリングすることでreadyStateプロパティの値を監視できます。
しかし注意しないといけないのは、このプロパティでリクエストが成功したかどうかは別問題だということ。4になってもそれは成功を意味するものではないのです。単にレスポンスが完了したことを示すだけです。
statusプロパティ
testRequest.status
通信が成功したかどうかはレスポンスのステータスで判断することになります。それを得るのが、statusプロパティ。このプロパティにはHTTPステータスコードが入ります。よくページが存在しない場合404と言いますが、この404はステータスコードです。
ステータスコードはリクエストがサーバでどう扱われたのかが判るようになっています。ステータスコードの大まかな意味は以下です。
ステータスコード群 | 大まかな意味 |
---|---|
100番台 | リクエスト処理を続行中である |
200番台 | リクエストが成功した |
300番台 | リダイレクト |
400番台 | リクエストが失敗した |
500番台 | サーバがエラーを起こした |
通常、リクエストに成功すると200が返ってきます。200以外の200番台でもおおむねリクエストの成功を表しますが、厳密には意味が異なるので、処理を詳細にしたい場合はステータスコードによる分岐処理をする必要はあります。
300番台はリクエストがリダイレクトされたことを表しますが、これも成功の内とみなすことができるかとおもいます。
今回は複雑な処理を行わないので、200,300番台はリクエストが成功したと見なします。
また同じように400番台、500番台は失敗を示すので、これらのステータスコードが返ってきた場合は失敗と見なすように処理をしています。
responseプロパティ
testRequest.response
レスポンス(サーバから返ってきた情報)はresponseプロパティで取得します。このプロパティはレスポンスの実体なので、サーバがテキストで返して来たらテキストが、JSON形式で返して来たらJSONで、Blobで返して来たらBlobが入ることになります。
もちろんどのような形式でレスポンスするかはサーバ側の実装になるので、クライアント側で決めることはできません。(無理やりパースしたりは出来なくはない)
その他テキスト形式でのレスポンスであるresponseTextプロパティ、DOMDocumentオブジェクト形式のレスポンスであるresponseXmlプロパティも存在しており、使い分けることも可能です。
openメソッド
testRequest.open("POST" , "https://unskilled.site/wp-admin/admin-ajax.php" , true );
このメソッドでリクエストを初期化する。第一引数はリクエストメソッドです。ほとんどの場合GETかPOSTになります。
wordpressが用意してくれているajax経路を使う場合、レスポンスに使いたい関数を特定するのに、”action=関数名”を送らなくてはいけません。(後述するsendメソッドで送信することになる)
もちろんURIにクエリとして付けることも可能なので、GETで送れなくはないですが、今回はリクエストを送る際に一応正規な方法であるPOSTを指定することにしました。(GETでの方法は後述)
第二引数はサーバのリソースの在りか、すなわちURIです。wordpressが用意してくれているajax経路の場合は、wp-admin/admin-ajax.phpになるので指定します。
第三引数は非同期か同期で通信するかを指定します。tureで非同期通信、falseで同期通信となります。前述したように、ajaxの利点は非同期通信、ノンブロッキング通信なので、基本的にはtureを選択します。
setRequestHeaderメソッド
testRequest.setRequestHeader("content-type", "application/x-www-form-urlencoded ; charset=UTF-8");
POSTでリクエストする場合は、HTTPヘッダ(コンテンツタイプ)を送らなくてはいけない。これはそういう決まりだからとしか言えないので、面倒だが仕方ないですね。
コンテンツタイプとは取り扱うデータの種類を指定するMIMEタイプという言葉と同義です。
POSTでリクエストを行うときは通常application/x-www-form-urlencodedを指定します。
このコンテンツタイプは名前からもわかるようにformデータを送るためのものです。
データ形式はformA=detaA&fomeB=detaBのように構成され、detaAやdetaBはURLエンコーディングが行われます。
ただ送りたいデータが必ずしもフォームデータだけということも無いかと思います。そこで代表的なコンテンツタイプを示しておきます。
コンテンツタイプ | 意味 |
---|---|
text/plain | プレーンテキストを送る場合 |
text/html | HTM文書を送る場合 |
application/xml | Xml形式データを送る場合 |
application/json | JSON形式データを送る場合 |
application/x-www-form-urlencoded | フォームデータを送る場合 |
sendメソッドchrasetが指定されていますが、デフォルトでUTF-8になります。だが念の為、明記しておいた方が安全かと思います。ちなみにUTF-8以外のセットの場合ほぼ間違いなく文字化けします。
testRequest.send( "action=testAjax" );
このメソッドでやっと送信開始ということになります。前述したように、POSTの場合にはこのメソッドで送信したいデータを指定します。今回はaction=testAjaxを文字列として送るります。
GETの場合
一応GETを指定した場合も載せておきます。
function ajax(){
var testRequest = new XMLHttpRequest();
testRequest.onreadystatechange = function (){
if( testRequest.readyState === 4 ){
if( ( 200 <= testRequest.status && testRequest.status < 300 ) ){
document.getElementById( "width" ).textContent = testRequest.response ;
}else{
console.log("リクエスト失敗");
}
}
};
testRequest.open("POST" , "http:/unskilled.site/pe/wp-admin/admin-ajax.php?action=testAjax" , true );
testRequest.send();
}
GETの場合にはメソッドをGETで指定して、wp-admin/admin-ajax.phpの後ろに?action=testAjaxのようにクエリを付けてあげればよいです。基本的にopenメソッド、sendメソッド、setRequestHeaderメソッドが変わるだけです。
GETの場合はHTTPヘッダーは必要ないです。本来送るデータが無いメソッドなのでコンテンツタイプ等を指定しなくてもよいからです。
もしGETでリクエストする場合、sendメソッドで何らかの送信データを指定しても無視されてしまいます(というか送れない)。
jQueryで書いてみる
今まで説明してきたネイティブでの書き方踏まえ、ネイティブでのデモコードと同等の機能をjQueryで書いてみると以下のようになります。
$.ajax({
type: "POST",
url: "https://unskilled.site/wp-admin/admin-ajax.php",
data:{ action : "testAjax"},
success: function( a ){
$( "#hoge" ).text( a );
},
error: function(){
alert("リクエスト失敗" );
},
complete: function(){
alert( "Ajax処理終了" );
}
});
ネイティブより全然短く済むし、何しろ見やすいです(何をやっているかわかりやすい)。やはりjQueryで書いたほうが楽なのは確かです。
ネイティブとの比較
ネイティブと比較してみよう。
type url
type: "POST",
url: "https://unskilled.site/wp-admin/admin-ajax.php",
typeはメソッドで、urlは送信先の指定。これらはネイティブだとopenメソッドで指定していたものになります。
data
data:{ action : "testAjax"},
次にdataですが、これはネイティブではsendメソッドで指定していたデータです。jQueryではオブジェクトで指定する形です。
success error complete
success: function( a ){
$( "#hoge" ).text( a );
},
error: function(){
alert("リクエスト失敗" );
},
complete: function(){
alert( "Ajax処理終了" );
}
さらにsuccess、error、completeはコールバック関数です。
successはネイティブで言うとstatusプロパティが4になり、レスポンスステータスが200番台で返ってきた場合に呼ばれるコールバック。つまり通信の終了と成功を意味しています。
successのコールバック関数の引数にはレスポンスデータが入るので、引数からデータを得て処理することができます。
errorはネイティブで言うとstatusプロパティが4になり、HTTPステータスコードが400番台(もしくは500番台)で返ってきた場合に呼ばれるコールバック。これは通信の失敗を意味します。
completeはネイティブで言うとstatusプロパティが4になった状態に呼ばれるコールバック。成功か失敗かに関わらず通信が完了したことを意味します。(ネイティブのデモコードにはこのcompleteに当たる処理は書いていない)
jQueryでのGET
jQueryではajaxメソッドとは別にgetメソッドが用意されています。まさにGETでリクエストするためのメソッドです。これはものすごく単純で、リクエスト先のURLとコールバック関数だけの簡素なものです。
$.get( "https://unskilled.site/wp-admin/admin-ajax.php?action=testAjax",
function( data ){
alert( data );
});
ただこのメソッドだと成功、失敗の処理が出来ないので、GETでもできるだけajaxメソッドで書いたほうがいいのではないかと思う。
jQueryのajaxメソッドのリファレンス
jQueryのajaxメソッドにはほかにも多くのプロパティが存在する。ここでは紹介しきれないので、ぜひ公式のリファレンス等で確認していただきたい。
- jQuery.com jQuery.ajax()
- http://api.jquery.com/jQuery.ajax/
ちょっとした応用編
すこしだけ実用的な処理
今までの説明はなるべく理解を優先して簡単な例を挙げたが実用性はないデス。そこで(ちょっとだけ、ほんとにちょっとだけ)実用性のある処理を説明してみたいと思います。ここでは最低限のセキュリティについても言及しようと思います。
JSON形式でレスポンスを得る
ajaxでよく使われるデータ形式はJSONです。このJSON形式はjavascriptと親和性が高いです。というのもJSON形式はjavascriptのオブジェクトの記述を元に制定されているからなんですね。ほとんどjsオブジェクトと同じと言ってもいいです。
JSON形式でレスポンスを受けるという応用をやってみたいと思います。
セキュリティに関して
クロスサイトリクエストフォージェリ(Cross site request forgeries,CSRF)という攻撃方法が存在するのをご存じでしょうか?ajaxは潜在的にこの攻撃方法にさらされやすいようです。
そこで、セキュリティとしてクライアントとサーバ間で暗号のやり取りをして、通信をチェックするという方法が取られます。この暗号のことをnonce(必ずしも暗号ではないようだが、予測困難なものでなくては意味がない為どちらにしろ暗号的になる)といいます。
サーバーがnonceを発行して、クライアントがそれを受け取り、nonceを含めてリクエストします。サーバがそのnonceが正規なものか(サーバが発行したものか)どうかチェックして、不正であれば弾く処理をします。
このnonceは基本的には一回の通信で使い捨てなので、ワンタイムトークンなどとも呼ばれるそうです。
nonseを使えば攻撃のすべて(CSRF以外にもいろいろ攻撃方法は存在する)を必ず防げるという訳ではないですが、少なくともCSRFに対しては効果はあるのは確かです。
困ったことにadmin-ajax.phpを経由する場合、nonceが無くても通信が成立します。なぜ強制しないのか不思議だが、利便性との兼ね合いもあるのでしょうか?ともかくnonceを使うにはその為の処理を書かなくてはいけません。
nonceを使うのは難しくは無く、以下のwordpress関数を使うことになります。
//nonce作成用関数
wp_create_nonce()
//ajax用のnonce確認用関数
check_ajax_referer()
余談ですが、wordpressを使わない場合、PHPでnonceを作る処理を自分で書く必要がある(ライブラリ等も存在しているようだが)。nonceは容易に推測されてはまずいので、nonceを作るにはそれ相応の暗号化に対する知識が必要になります。
何かを元に暗号を作るにしても、その元が推測されては意味がないからです。推測され難く、確実にチェックできるするための処理が必要になります。
その点wordpressでは指定したアクションやユーザID、時刻やwodpressが独自に持っている暗号化の為の情報がいくつか併用され、かなりがっちりとしたnonceを作ってくれます。。暗号化の知識に自信が無くても、手軽に強固なnonceを扱えるところがうれしいですね。
コード
実用的とか言っておきながら、ほとんど実用的ではないことに関しては黙秘します。このajaxでは存在する投稿(POST)をランダムに選び、タイトル、公開日時、投稿IDをJSON形式で返し表示するというものです。
簡素極まりないがデモページはこちらから。
サーバ側(functions.php)ネイティブとjQuery共通
add_action( "wp_ajax_testAjax" , "testAjax" );
add_action( "wp_ajax_nopriv_testAjax" , "testAjax" );
function testAjax(){
check_ajax_referer( "testAjax" );
global $wpdb;
$myrows = $wpdb->get_results( "SELECT ID FROM $wpdb->posts WHERE post_type=\"post\" AND post_status=\"publish\"" );
$postCount = count($myrows);
$rand = rand( 0 , $postCount - 1 );
$postObject = get_post( $myrows[$rand]->ID );
$json[] = array(
"title"=> $postObject->post_title,
"id"=> $postObject->ID,
"post_date"=> $postObject->post_date,
);
header( "Content-Type: application/json; X-Content-Type-Options: nosniff; charset=utf-8" );
echo json_encode( $json );
die();
}
}
クライアント側(javascriptとnonceの発行)ネイティブの場合
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<style>
body{
padding: 5%;
}
#ajaxDiv,
#title,
#post_deta,
#id{
margin-bottom: 2%;
}
</style>
<script>
function nativeAjax( data ){
var testRequest = new XMLHttpRequest();
testRequest.onreadystatechange = function (){
if( testRequest.readyState === 4 ){
if( ( 200 <= testRequest.status && testRequest.status < 300 ) ){
var responseObject = JSON.parse( testRequest.response );
document.getElementById( "title" ).textContent = responseObject[0]["title"] ;
document.getElementById( "post_deta" ).textContent = responseObject[0]["post_date"] ;
document.getElementById( "id" ).textContent = responseObject[0]["id"] ;
}else{
console.log("リクエスト失敗");
}
}
};
testRequest.open( "POST" , "https://unskilled.site/wp-admin/admin-ajax.php" , true );
testRequest.setRequestHeader( "content-type", "application/x-www-form-urlencoded ; charset=UTF-8" );
testRequest.send( data );
}
window.onload = function(){
document.getElementById( "ajaxDiv" ).addEventListener( "click" , function(){
//グローバルに出たオブジェクトからアクション名とnonceを拾う
var request = "action=" + data.action + "&" + "_ajax_nonce=" + data.nonce;
return nativeAjax( request );
});
};
</script>
<title>ajaxデモ</title>
</head>
<body>
<?php
$action = "testAjax";
$nonce = wp_create_nonce( "testAjax" );
//グローバルにオブジェクトを出す
echo "<script>var data = { nonce : \"" . $nonce . "\" , action : \"" . $action . "\"}</script>";
?>
<div id="ajaxDiv">
ここをクリックしてください
</div>
<div id="title">
タイトル
</div>
<div id="post_deta">
公開日
</div>
<div id="id">
記事ID
</div>
</body>
</html>
クライアント側(javascriptとnonceの発行)jQueryの場合
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js" ></script>
<style>
body{
padding: 5%;
}
#ajaxDiv,
#title,
#post_deta,
#id{
margin-bottom: 2%;
}
</style>
<script>
$( window ).on("ready" , function(){
$( "ajaxDiv" ).on( "click" , function(){
$.ajax({
type: "POST",
url: "https://unskilled.site/wp-admin/admin-ajax.php",
data:{ action : data.action,
_ajax_nonce : data.nonce },
success: function( a ){
$( "#title" ).text( a[0]["title"] );
$( "#post_deta" ).text( a[0]["post_date"] );
$( "#id" ).text( a[0]["id"] );
},
error: function(){
console.log("リクエスト失敗" );
},
complete: function(){
console.log( "Ajax処理終了" );
}
});
});
});
</script>
<title>ajaxデモ</title>
</head>
<body>
<?php
$action = "testAjax";
$nonce = wp_create_nonce( "testAjax" );
//グローバルにオブジェクトを出す
echo "<script>var data = { nonce : \"" . $nonce . "\" , action : \"" . $action . "\"}</script>";
?>
<div id="ajaxDiv">
ここをクリックしてください
</div>
<div id="title">
タイトル
</div>
<div id="post_deta">
公開日
</div>
<div id="id">
記事ID
</div>
</body>
</html>
補足説明
まずはクライアント側の処理から説明していきたい。説明はネイティブとjQuery一緒に行う。
nonceの作成
<body>
<?php
$action = "testAjax";
$nonce = wp_create_nonce( "testAjax" );
//グローバルにオブジェクトを出す
echo "<script>var data = { nonce : \"" . $nonce . "\" , action : \"" . $action . "\"}</script>";
?>
body直下でphpを何行か書いていますが、ここは送信するデータを用意している部分です。ajaxの標的であるアクション名testAjaxは変わらないですが、もう一つ下記の部分でnonceを作っています。
$nonce = wp_create_nonce( "testAjax" );
アクション名とnonceはオブジェクトにして、echoで<script>タグに仕立ててグローバルに吐き出しています。
あまりグローバルスコープを汚すものではないですが、オブジェクトにしてしまえば、名前一個分だけの汚染で済みます。それくらいは黙っておこうという流儀です。(フォームを使用していないのは@MINOがフォームを憎んでいるから)。
ここまではネイティブでもjQueryでも変わらないです。
javascriptの処理
//ネイティブ
if( ( 200 <= testRequest.status && testRequest.status < 300 ) ){
var responseObject = JSON.parse( testRequest.response );
document.getElementById( "title" ).textContent = responseObject[0]["title"] ;
document.getElementById( "post_deta" ).textContent = responseObject[0]["post_date"] ;
document.getElementById( "id" ).textContent = responseObject[0]["id"] ;
}else{
console.log("リクエスト失敗");
}
//jQuery
success: function( a ){
$( "#title" ).text( a[0]["title"] );
$( "#post_deta" ).text( a[0]["post_date"] );
$( "#id" ).text( a[0]["id"] );
},
ajax通信の状態や成功失敗に関しては変えていないです。成功した場合の処理は、各DIVの内容を取得したレスポンスの内容で書き換えているだけです。jQueryでの処理も同じで、textメソッドで書き換えを行っているだけです。
var responseObject = JSON.parse( testRequest.response );
ネイティブでは成功した場合、JSON.parseというjavascript関数が出てきていますが、これはJSON形式のデータをjsのオブジェクト形式に変換する為です。これでjavascriptでサーバからのレスポンスを自由に扱うことができます。
jQueryの場合は勝手にちゃんとパースしてくれるので、自分で処理をする必要はないデス。
window.onload = function(){ document.getElementById( "ajaxDiv" ).addEventListener( "click" , function(){
//グローバルに出たオブジェクトからアクション名とnonceを拾う
var request = "action=" + data.action + "&" + "_ajax_nonce=" + data.nonce; return nativeAjax( request ); });
onloadイベント内での処理としてはsend()で送る為のデータの体裁を作っている。作られたnonceを送るとき、_ajax_nonceというnameにする必要があるようです。
こうすることによってわざわざ_ajax_nonceを$_POST[“_ajax_nonce”]のような感じで、引き抜いてこなくてもwordpress(check_ajax_referer)が勝手にnonceを引っ張ってくれます。
jQueryの場合ではonメソッドでページの読み込みを待ってからですが、ネイティブのonloadと基本的には変わらないです。クリックイベントに関しても同様です。またグローバルに出したアクション名とnonceはそのままオブジェクトプロパティとして指定しています。
後はネイティブでもjQueryでも#ajaxDivがクリックされればnativeAjaxが動いてajax通信が始まります。
サーバ側の処理
少しだけ複雑になっていますが、ajax自体に関わるのは以下の部分です。
check_ajax_referer( "testAjax" );
header( "Content-Type: application/json; X-Content-Type-Options: nosniff; charset=utf-8" );
echo json_encode( $json );
check_ajax_referer
check_ajax_refererはnonceのチェックを行う関数。これにajaxのアクション名を与えれば、クライアントから送ったnonceが正規のものかを確かめることができます。
nonceはさまざまな要素(wordpressでは時刻やユーザIDやアクション名やその他難読化するためハッシュ値など)で暗号化されていますが、要素がわかれば復号もできます。その為、サーバ(wordpress)は送られてきたnonceが正しいのかを判断できるわけです。
check_ajax_refererはもしnonceが不正規だった場合、強制的にdie( ‘0’ )で関数を終わらせる仕組みになっているので、特に条件式を組んで場合分けをする必要はないです。処理が始まるまえにcheck_ajax_refererが動けばいいので、関数定義の一番上に置くのがいいのではないかと思います。
コンテンツタイプ
今回はJSON形式でクライアントにデータを送るので、それ用ヘッダーも必要になります。JSON形式を送る為にはコンテンツタイプを「Content-Type: application/json;」にする。「X-Content-Type-Options: nosniff」というのがくっついているが後述します。
json_encodeなど
最後にPHPのjson_encode関数でデータをJSON形式にエンコードして送信しています。
実はwordpressにはJSON形式データを送る為のwp_send_json関数と、JSON形式データをエンコードするwp_json_encode関数が用意されています。wp_send_jsonに関してはヘッダーを自ら付ける必要が無く、関数内で用意してくれます。
また送信されるレスポンスデータの形式を自動認識するブラウザの機能を停止する「X-Content-Type-Options: nosniff」の付加も自動でやってくれます。
これは誤ったデータ形式を故意に認識させるXSS攻撃に対して有効な手段。その為、可能な限りwp_send_jsonを使った方が楽だし安心だと思います。
wp_json_encode関数に関しても内部でデータのチェックをしているようなので(リファレンスには「with some sanity checks」とある)、PHPの関数であるjson_encodeを使うよりもいいのかもしれません。
今回の例に含めなかったが、関数をご自分でも調べていただけると理解が深まること請け合いです。
その他
ajaxには直接関係ないが、下記の辺りの処理が気になる方もいるかもしれないので軽く説明だけしておきたいです。
$myrows = $wpdb->get_results( "SELECT ID FROM $wpdb->posts WHERE post_type=\"post\" AND post_status=\"publish\"" );
$postCount = count($myrows);
$rand = rand( 0 , $postCount - 1 );
$postObject = get_post( $myrows[$rand]->ID );
$json[] = array(
"title"=> $postObject->post_title,
"id"=> $postObject->ID,
"post_date"=> $postObject->post_date,
);
$wpdbはwordpressでデータベースを扱う為のオブジェクトで、このオブジェクトを使うとSQLを簡単に発行でき、データベースから情報を持ってくる事ができます。
「”SELECT ID FROM $wpdb->posts WHERE post_type=\”post\” AND post_status=\”publish\””」というのがデータベースに問い合わせる為のSQL文というものです。
意味としては「postsというテーブルで、post_typeがpost、post_statusがpublishとなっているIDを返してください」になります。つまり公開されている投稿のIDをよこせと言っているのです。
このSQL文で得たID群を$rand関数でランダムに選び出して、タイトル、公開日時を取得して、JSON形式にしているという感じです。
もっと実用的な事を考えようと頑張ったのですが、@MINOの脳細胞は一日に30分くらいしか働かないようで、愚にもつかない例になってしまったことはこっそりお詫びします。
まとめ
さてながながと説明してきたが、wordpressでajaxを使う際の基本的な内容はお伝えできましたでしょうか?
できるだけajaxの基本を伝えようと頑張ましたが、ネイティブの場合のクロスブラウザ対応の件とか、さらにセキュアなajaxの方法、クロスドメインでのajaxに関して、wordpressの独自ajax経路に関して、エラーに関しての対処や確認の方法などなど書き足りない部分もたくさんあります。
これらは別途記事にしていきたいと思います。
ajaxはもやはどこでもどんな用途でも使われてる為、初心者にとって意外と実体を把握しにくいと言わざるを得ないです。@MINOなど未だに全体像が分からないですよ。
適用範囲が広大なので、複数のやり方が存在するのも確か。今回紹介した例は馬鹿正直に書いている例でもあり、現在はjQueryでももっとモダンな書き方をする人が多いです。
@MINOが書くような価値のないコードは論外としても、人によってかなり書き方が異なるだろうと思います。
モダンな書き方がいいかどうかは別としても、まずはちゃんと理解しなくては応用的な書き方にも進めませんからね。
この長ったらしい記事が少しでも役に立ったならこれ幸いです。