[WordPress] 開發 AJAX 功能的正確流程

根據功能或是加強前端體驗的需求,AJAX這個作法很常見。要做到在 WordPress 系統提供的方法也不難,如下流程。

定義後端方法與功能

function mxp_ajax_get_next_page_data() {
    $max_num_pages = $_POST['max_num_pages'];
    $current_page = $_POST['current_page'];
    $found_posts = $_POST['found_posts'];
    $nonce = $_POST['nonce'];
    if (!wp_verify_nonce($nonce, 'mxp-ajax-nonce')) {
        wp_send_json_error(array('code' => 500, 'data' => '', 'msg' => '錯誤的請求'));
    }
    if (!isset($max_num_pages) || $max_num_pages == "" ||
        !isset($current_page) || $current_page == "" ||
        !isset($found_posts) || $found_posts == "") {
        wp_send_json_error(array('code' => 500, 'data' => '', 'msg' => '錯誤的請求'));
    }
    $ids = get_posts(array(
        'fields' => 'ids', // Only get post IDs
        'posts_per_page' => get_option('posts_per_page'),
        'post_type' => 'post',
        'paged' => intval($current_page) + 1,
    ));
    $str = '';
    foreach ($ids as $key => $id) {
        $name = get_post_meta($id, 'wctp2018-author-name', true);
        $title = mb_substr(get_post_meta($id, 'wctp2018-post-title', true), 0, 20);
        $content = mb_substr(get_post_meta($id, 'wctp2018-post-content', true), 0, 40) . "...";
        $image_large = get_post_meta($id, 'wctp2018-post-image-large', true);
        $str .= '<div class="col-md-3 m_b_20 post"><div class="box"><div class=" post_img"><a href="' . get_permalink($id) . '"><img src="' . $image_large . '"/></a></div><a href="' . get_permalink($id) . '" class="name"><h2 >' . $content . ' - ' . $name . '</h2></a></div></div>';
    }
    wp_send_json_success(array('code' => 200, 'data' => $str));
}
add_action('wp_ajax_nopriv_mxp_ajax_get_next_page_data', 'mxp_ajax_get_next_page_data');

後端有兩個關鍵要注意: wp_verify_nonce 方法與 wp_ajax_nopriv 的 hook。

wp_ajax_nopriv 是一個組合事件,以此開頭的 AJAX 事件為「不需登入即可使用」的做法,少了 nopriv 關鍵字就是必須要登入才可以使用的方法,而 wp_verify_nonce 方法是用來驗證 CSRF Token ,避免跨站隨意請求。

整個 AJAX 的後端方法是用在 WordCamp Taipei 活動站作為撈取文章附加列表頁面用。而下方還需要從後端定義一些 JS 組合用的變數

wp_enqueue_script('main', get_template_directory_uri() . '/js/main.js');
wp_localize_script('main', 'WCTPE', array(
    'ajaxurl' => admin_url('admin-ajax.php'),
    'nonce' => wp_create_nonce('mxp-ajax-nonce'),
));

注意到 wp_create_nonce 在這邊就是帶入 Token 變數的地方,而 ajaxurl 路徑是讓未登入狀態下還能對應請求位置。內建的系統流程發送 AJAX 請求都是這個腳本位置。

定義前端方法與功能

$('.more_posts').click(function() {
            $(this).attr('disabled', true);
            var that = $(this);
            if (WCTPE.posts.max_num_pages == WCTPE.posts.current_page) {
                $(this).text('The END! / 最後一頁');
                $(this).unbind('click');
                return;
            }
            $("body").waitMe({
                effect: "bounce",
                text: "Loading...",
            });
            var data = {
                'action': 'mxp_ajax_get_next_page_data',
                'nonce': WCTPE.nonce,
                'max_num_pages': WCTPE.posts.max_num_pages,
                'current_page': WCTPE.posts.current_page,
                'found_posts': WCTPE.posts.found_posts,
            };

            $.post(WCTPE.ajaxurl, data, function(res) {
                if (res.success) {
                    $('.tattoo_posts_lists').append(res.data.data);
                    history.pushState(null, null, '/page/' + (WCTPE.posts.current_page + 1) + '/');
                    WCTPE.posts.current_page += 1;
                    that.attr('disabled', false);
                } else {
                    alert('Oops! Sorry error occurred!');
                    location.reload();
                }
                $("body").waitMe('hide');
            }).fail(function() {
                alert('Oops! Sorry error occurred! Internet issue.');
            });
        });

JavaScript 請求資料 data 中的 action 是固定的參數,指名對應後端的方法,少了事件前綴。 WCTPE 變數即是在 wp_localize_script 方法中所建置的 JavaScript 變數。

結語

總體來說,如上三大程式區塊,雖然個別有小細節要注意,但這樣一個前後端搭配的流程就是走一套正確的 WordPress 架構開發。雖然官方文件這部分是放在「外掛」中,但其實主題也是通用的~ 可以藉此把體驗做到最好!

Ref: AJAX in Plugins

Facebook 外掛整合


Share: