WordPress AJAX佈景實作 (以jQuery為例)


作者: | 2012/06/12 | 1 則迴響


wordpress ajax

一般使用jQuery實作AJAX的情況

如果你曾經在一般網頁上實作過ajax效果,尤其是使用jQuery (無論是用load(), post()或ajax()…等方式),你一定可以在閱讀本文後很快就理解WP是怎麼處理ajax效果的。

先來看看一個利用 jQuery Ajax 函式傳遞資料到後端的例子:

$.ajax({
	type: 'POST',
	url: 'https://my_ajax_url.test/ajax.php',
	data: {
		arg: 'arg data'
	},
	success: function(res) {
		console.log(res); // 後端回傳結果
	},
	error:function (jqXHR, ajaxOptions, thrownError){
		console.log(ajaxOptions+':'+thrownError);
	}
});

以jQuery實作AJAX為例,我們通常會以送出GET或POST請求的方式,帶上一些參數資料,再丟給一個負責處理的目標網址,而這個目標網址的PHP檔會接收我們傳過去的參數,之後再進行後端的各種處理,完成後端處理時,後端 PHP 也要回報結果給前端,前端如果成功收到結果,就會觸發 success 的 function;反之如果 Ajax 呼叫的過程中發生問題,則會觸發 error 的 function。

$.ajax 函式有非常多參數可設定,以下說明為求簡單換成 $.post,一樣都是 Ajax 呼叫,只是 $.post 預設就走 POST,可設定的參數和事件也比較少:

$.post(
	'https://my_ajax_url.test/ajax.php', 
	{
		arg: 'arg data'
	}, 
	function(res) {
		console.log(res); // 後端回傳結果
	}
);

在WP實作AJAX的概述

在WP實作ajax的效果,大致也是採用這種方式,但是無論前台、後台中的任何一個頁面,都統一要將參數資料丟到 https://wp網址/wp-admin/admin-ajax.php 這個目標網址。當你把資料丟過去之後,你就可以在functions.php或者plugin裡利用hook function,去取得丟過來的參數資料。

請注意,本文以下的例子為求容易理解原理,因此沒有考慮最佳的實作方式,建議你應該在理解運作原理後多多參考其他更好的作法。

在前台佈景實作AJAX效果

為求方便講解,我們直接修改佈景檔中的 header.php (請先確定你的WP網站已有載入jQuery),在<head>裡加入:

<script>
 jQuery(function(){
   // 按下按鈕後開始送出POST要求
   $('.btn_ajax').click(function () {
     $.post('<?php echo admin_url( 'admin-ajax.php' );?>', {
       action: 'my_ajax_action', // 自取一個action的名稱
       post_id: $('#my_id').val() // 附上的參數
     }, function(data) {
       alert(data); // 當AJAX處理完畢,就把回傳的資料alert出來
     });
   });
 });
</script>

裡頭的PHP函式:admin_url(‘admin-ajax.php’) 就是回傳剛才提到的 wp ajax 統一處理窗口 https://wp網址/wp-admin/admin-ajax.php 的完整網址;然後我們要自己取一個action的名稱,之所以叫action,就是表示這裡的名稱會對應到等會要寫的hook function的名稱。

接著在header.php下方<body>之後找地方加入:

<input type="text" value="mrmu" id="my_id">
<input type="button" class="btn_ajax" value="按我">

如此前端的配置就完成了,接著到 functions.php找地方加入:

add_action( 'wp_ajax_my_ajax_action', 'ajax_action_stuff' ); // 針對已登入的使用者
add_action( 'wp_ajax_nopriv_my_ajax_action', 'ajax_action_stuff' ); // 針對未登入的使用者

function ajax_action_stuff() {
  $post_id = $_POST['post_id']; // 從ajax POST的請求取得的參數資料
  echo $post_id; // 單純的印出來,如此前端就會收到
  die(); // 一定要加這行,才會完整的處理ajax請求
}

觀察一下上述的程式碼,首先我們加入兩個action hook,第一個是wp_ajax_my_ajax_action,去掉前綴字’wp_ajax_’,剩下的my_ajax_action有沒有很眼熟?就是我們在前端自取的action名稱。
第二個action hook也是拿了我們自取的action加上前綴字’wp_ajax_nopriv_’,代表若使用者未登入,就會觸發這個hook。

當wp_ajax的hook function成功被觸發時,我們就能拿到前端丟過來的參數資料,也就能拿它來作為條件,進一步去存取資料庫,再回傳值至前端,達成ajax的效果。

不過,進一步的看這個例子,其實是很多缺點的。我們的例子是把前端js的部份寫在header.php,所以可以偷吃步的使用php function來取得admin-ajax.php的完整網址,如果要把這段js程式碼整理到.js檔內,就要在functions.php這樣寫了:

add_action('init', 'add_my_ajax_script');
function add_my_ajax_script(){
  wp_enqueue_script( 
    'my_ajax_script', 
    get_stylesheet_directory_uri().'/js/script.js', 
    array('jquery'), 
    1.0 
  );
  // 將 ajaxurl 定義於前端頁面,讓 javascript 可以取得 PHP 定義的後端資料
  wp_localize_script( 
    'my_ajax_script', 
    'my_ajax_obj', 
    array( 
      'ajaxurl' => admin_url( 'admin-ajax.php' ) 
    )
  );
}

如此我們就可以在佈景目錄下的/js/script.js中利用my_ajax_obj.ajaxurl取得admin-ajax.php的完整網址:

(function($) {
 $(function() {
   $('.target').click(function () {
     $.post(my_ajax_obj.ajaxurl, { //取得admin-ajax.php的完整網址
       action: 'ajax_action',
       post_id: $(this).find('input.post_id').attr('value')
     }, function(data) {
       alert(data); // alerts
     });
   });
 });
})(jQuery);

在後台實作AJAX效果

在需要作用的頁面上,註冊需要的style及script

要在wp後台完成ajax效果,我們必須要先在目標頁面加入我們自訂的JS。你可能想在發表文章頁、編輯文章頁加入ajax效果,或者自訂一個後台設定頁面,然後在裡頭使用ajax效果。

你當然可以直接用admin_init這個hook,來將所需的js及css檔加入,但如此一來在後台所有頁面都會匯入你自訂的js及css檔。講究一點的話,你應該只針對想要有效果的頁面來匯入,如果是發表/編輯文章頁面,可以利用這兩個action hook:

add_action('load-post.php', 'reg_admin_scripts_and_styles' ); // 文章編輯頁
add_action('load-post-new.php', 'reg_admin_scripts_and_styles' ); // 文章發表頁

若是自行建立新的後台頁面,則可以在呼叫建立頁面的function後取得page_hook_suffix,並利用它來加入所需的js及css,以建立一個「設定」頁面來舉例:

add_action('admin_menu', '__add_menu' );
function __add_menu()
{
  if (function_exists('add_options_page'))
  {
    $my_opt_page_hook_suffix = add_options_page(
      'Product', //Page Title
      'Tcc CP Product', //Menu Title
      'editor', // Capability
      'my_menu_slug', // Menu Slug
      'panel_ui_rendering' // UI Function
    );

    // 在此頁enqueue頁首script及styles
    add_action("load-{$my_opt_page_hook_suffix}", 'reg_admin_scripts_and_styles' );
  }
}

然後你就可以建立reg_admin_scripts_and_style 這個function,來將所需的scripts及styles註冊及enqueue。

發出及傳送資料給WP

針對特定頁面來匯入所需的js後,就可以進入正題了,要怎麼完成ajax效果?跟前台一樣。

首先,匯入的 js 裡,無論使用jQuery load, ajax, post,都一定要 POST 一個名為action的變數給WP已預先定義的網址:ajaxurl。
跟前台稍微不同的地方是,後台的ajaxurl變數,已經先幫我們取得admin-ajax.php的完整網址了,所以直接拿來用就行了。
action變數的內容需要自訂一個名稱,而WP就會以這個名稱偷偷建立對應的hook action。

除了post action外,可以再自帶需要的參數進去,通常會建立一個名為data的js object,舉例來說,你的js檔內相關處理的code可能會長這樣:

(function($) {
  $(function() {
    // 要POST的資料
    var data = {
      action: 'list_rec',
      arg1: 'arg1 的資料',
      arg2: 'arg2 的資料',
      arg3: 'arg3 的資料',
    };
    // 自 WP 2.8 起ajaxurl 已被定義在 admin header 並指向 admin-ajax.php
    $('#ajax_content_sec').load(ajaxurl, data, function(){
    // 已將data物件POST到ajaxurl,ajax 載入成功!
      alert('載入完畢!');
    });
  });
})(jQuery);

接著在functions.php或plugin裡,就可以直接add你在js裡定義的那個action,只是要記得加上前綴字wp_ajax_:

add_action('wp_ajax_list_rec', 'list_rec_callback');
function list_rec_callback() {
  // 讀取POST資料
  $arg1 = $_POST['arg1'];
  $arg2 = $_POST['arg2'];
  $arg3 = $_POST['arg3'];

  global $wpdb; //可以拿POST來的資料作為條件,撈DB的資料來作顯示
  echo 'arg1: ' . $arg1 . ', arg2: '. $arg2 . ', arg3: '. $arg3;
  die(); // this is required to return a proper result
}

有了這樣的架構,我們就可以實作ajax效果了。最普遍的例子就是在使用者按下連結或按鈕後,會出現Loading的動畫,然後某個區塊就產生一堆資料。

這樣的行為,需要在js收集好「條件」,可以在js裡針對某個連結來bind一個click事件,然後將dom裡的資料(或url上的GET參數)拿來放進上述的data物件裡,再作ajax post。

post到wp_ajax 對應的hook function裡時,就可以拿到這些「條件」,接著在wp_ajax function裡利用$wpdb撈出所需的資料,再作輸出就打完收工了。

上述的程式碼只是方便說明,還有很多進步空間。比如我自己實際 coding 時,通常會用 $.ajax 的方式來傳值,dataType 通常指定 json 居多,如此後端 ajax function 在回傳結果時可以帶入自訂的狀態碼、資料、錯誤訊息等,能讓前端方便判斷 json 裡的資料再作進一步反應,例如:

public function ajax_action_stuff() {
	if (empty($_POST['arg'])) {
		$error = new WP_Error( 'arg_empty', 'Error: No data.', $this->plugin_name );
		wp_send_json_error( $error ); // 回傳json格式的錯誤訊息
	}

	$arg = sanitize_text_field($_POST['arg']); // 永遠不要相信前端使用者輸入的資料都是善意的

	/* 拿到前端傳來的資料了,看你要在後端做什麼處理... */

	// 處理完了,要通知前端處理結果...
	$return = array(
		'message' => __( 'Success', $this->plugin_name ),
		'data' => $arg
	);
	wp_send_json_success( $return ); // 回傳json格式的成功訊息
	// 就不用再執行 die() 了
}

標籤:

分類:, ,

本文作者是Audi Lu

1 則留言

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *

*

*

*

這個網站採用 Akismet 服務減少垃圾留言。進一步了解 Akismet 如何處理網站訪客的留言資料