Використання AJAX-обробника WordPress



WordPress, будучи одній з найпопулярніших CMS в світі, забезпечений докладною документацією, а точніше, навіть двома. У зв'язку з чим ні в якому разі не варто сприймати цей текст як опис якихось «best practices» і вже точно ніхто не змушує сліпо слідувати описаного. Стаття — просто швидкий відповідь на питання «як?!» (наступний абзац) і докладний опис усього, що потрібно знати щоб змусити WordPress відповідати на AJAX-запити (вся інша стаття).

Коротко
Традиційно для AJAX-запитів потрібно дві речі: скрипт на сервері (бекенд), який буде відповідати на запити, і скрипт на клієнті (фронтенд), який буде ці запити робити. WordPress дозволяє делегувати функції на звернення до спеціального URL, за яким знаходиться обробник запитів.

Отже, це працює, «WordPress-way», ось так:

  1. На бекенде за допомогою функції
    admin_url
    отримуємо посилання на обробник AJAX-запитів і передаємо її в фронтенд одним із способів. Саме до цієї ссылке ми будемо робити наші запити.
  2. На бекенде реєструється хук з функцією для обробки якогось екшену. Назвемо цей екшен, наприклад, get_posts.
  3. Фронт-енд робить запити до URL-у з пункту 1, передаючи ім'я екшену. Наприклад,
    ?action=get_posts
    .
    На бек-енде, якщо на екшен зареєстрований хук, виконується задана нами функція.


Ось так просто. Тепер детальніше.

Детальніше
AJAX-обробник на бекенде
Деякі люди роблять AJAX-обробник вручну, шляхом инклюда файлу wp_load. Це вважається дуже поганою практикою по цілій купі причин. У WordPress є свій, готовий обробник. Будьте паіньками і користуйтеся ним.

Після того, як фронтенд «дізнався», куди йому слати запити, потрібно додати на бекенде хук для оброблення цих запитів. Обов'язковий параметр в цих запитах —
action
: цей параметр визначає, що саме ми "хочемо" від бек-ендом.

Для того, щоб створити новий метод AJAX-обробника, потрібно повісити два хука:
wp_ajax_<ім'я екшену>
та
wp_ajax_nopriv_<ім'я екшену>
. Наприклад, ось так:

add_action('wp_ajax_get_posts' , 'get_posts_callback');
add_action('wp_ajax_nopriv_get_posts', 'get_posts_callback');

Префікс
wp_ajax_
спочатку імені хука дасть WordPress зрозуміти, що ми намагаємося створити обробник AJAX-запиту. Префікс
wp_ajax_nopriv
дозволяє зареєструвати хук для незалогиненных користувачів. Таким чином можна зареєструвати різні обробники для залогиненных і незалогиненных юзерів, що може бути зручно. При цьому, якщо вам потрібно, щоб ajax-запит виконувався і для тих, і для інших, вам доведеться повісити одну функцію на обидва хука.

Ці хуки — звичайні, такі ж, як всі інші WP-хуки. У результаті реєстрації таких хуків при зверненні до URL-у
wp-admin/admin-ajax.php?action=get_posts
має виконатися функція
get_posts_callback
.

add_action( 'wp_ajax_do_something', 'get_posts_callback' ); // For users logged in
add_action( 'wp_ajax_nopriv_do_something', 'get_posts_callback' ); // For anonymous users

function do_something_callback(){
echo(json_encode( array('status'=>'ok','request_vars'=>$_REQUEST) ));
wp_die();
}

В результаті виконання цього коду за посиланням виду
wp-admin/admin-ajax.php?action=do_something
має виводитися шматок JSON-а.

Деякі особливості на бекенде
Напевно, має сенс зауважити, що хуки в WordPress вішаються кожен раз. Тобто обробник потрібно реєструвати кожен раз, бажано у файлі, який включається кожний раз (у випадку з темою цей файл functions.php) Якщо спробувати зареєструвати AJAX-хук, наприклад, у файлах page.php або index.php теми, хук не буде працювати, тому що при зверненні до обробника ці файли, зрозуміло, не будуть виконуватися.

Рекомендується всі функції, повішені на екшени AJAX-обробника, закінчувати викликом
wp_die
або функції
wp_send_json_success
та аналогічних. Або простий
die
, на худий кінець, викликати.

У випадку помилки обробник запитів повертає код 0 або -1 залежно від причини помилки. Зокрема, у разі, якщо такого хука не існує (тобто він не був зареєстрований з якоїсь причини) — повертається 0.

Функція
is_admin
, яка повертає true, якщо користувач знаходиться в адмінпанелі сайту, будучи викликаною з AJAX-обробника, завжди повертає true.

Існує функція
check_ajax_referer
, яка перевіряє реферера (перевіряє, звідки стався запит) і перериває виконання, якщо реферер якийсь «не такий». Ця функція вміє також перевіряти
nonce. Детальніше можна почитати у відповідній статті кодексу.

Передача посилання на фронтенд
Адреса оброблювача запитів — щось типу
wp-admin/admin-ajax.php
. Фішка в тому, що наш фронтенд "не знає" адреси посилання. Тому, якщо ми хочемо, щоб наша тема або плагін була максимально універсальною і портативної — нам потрібно отримати в WordPress і передати на фронтенд актуальний адреса посилання:

$ajax_url = admin_url('admin-ajax.php');

На фронтенд її можна передати різними способами. Найчастіше це посилання — не єдине, що потрібно передавати на фронтенд, тому я віддаю перевагу вставляти її з допомогою відповідного хука в wp_head в теге
<script>
:

// десь у functions.php
function js_variables(){
$variables = array (
'ajax_url' => admin_url('admin-ajax.php'),
'is_mobile' => wp_is_mobile()
// Тут зазвичай якісь інші змінні
);
echo(
'<script type="text/javascript">window.wp_data = ',
json_encode($variables),
';</script>'
);
}
add_action('wp_head','js_variables');

Цей хук буде виводити тег
<script>
з реєстрацією глобальних JS-змінних з посиланням на обробник у в секції
<head>
теми (зрозуміло, для цього потрібно викликати в ній функцію
wp_head
, що в будь-якому випадку рекомендується робити для будь-якої теми)

Тег буде вставлятися одразу після всіх підключених ассетов (скриптів і таблиць стилів, зареєстрованих через систему ассетов WordPress)

Передати посилання на фронтенд можна також за допомогою функції
localize_script
. Я знаходжу такий підхід дещо тужно: де-факто, ми відправляємо на фронтенд не наш джаваскрипт з папки assets теми або з якого-небудь CDN, але його модифіковану версію, при цьому не залишаючи користувачеві вибору (який у нього є, наприклад, у випадку з плагінами, що склеюють всі скрипти в один і полягає в тому, використовувати йому ці плагіни чи ні). Якщо що — докладніше про підході з
localize_script
— в кодексі вордпресс, у статті AJAX In Plugins.

І, зрозуміло, можна НЕ передавати цю посилання на фронтенд, а просто її або її частина захардкодить прямо в JS-файл. Але ніхто не гарантує, що в наступній версії WordPress або просто на якомусь іншому конкретному энвайрменте URI обробника буде саме 'wp-admin/admin-ajax.php'. Для універсальності і сумісності рекомендується завжди використовувати функцію admin_url для отримання актуальної посилання на обробник і передавати її у фронтенд вручну.

До речі, на всіх сторінках адмінки це посилання вже проброшена в глобальну змінну ajaxurl в JS. Деякі плагіни вивішують це посилання для сторінок сайту, але на це не варто розраховувати (адже ми не хочемо зайвих залежностей для нашого плагіна/теми, правда?)

фронтенде
Тепер ми можемо досить легко робити веб-запити до бекенду з нашого фронтенда. Наприклад, якщо посилання ми вивісили в змінну wp_data.ajax_url і підключений jQuery — це буде виглядати приблизно ось так:

jQuery(function($){
$.ajax({
type: "GET",
url: window.wp_data.ajax_url,
data: {
action : 'do_something'
},
success: function (response) {
console.log('AJAX response : ',response);
}
});
});

Якщо все зроблено правильно — при запуску виконається веб-запит до бекенду, який відповість шматком JSON-а, який потім буде отримано фронтендом і виведений в консоль.

Любителям ООП
Якщо ви намагаєтеся побудувати свою власну архітектуру теми, з автозагрузчиком і ОВП, можна визначити абстрактний клас ось такого типу:

abstract class AJAX_Handler {

function __construct($action_name) {
$this->init_hooks($action_name);
}

public function init_hooks($action_name) {
add_action('wp_ajax_'.$action_name , array($this,'callback'));
add_action('wp_ajax_nopriv_'.$action_name, array($this,'callback_nopriv'));
}

public function callback_nopriv() {
$this->callback();
}

abstract public callback function();

}

Тепер достатньо створити для кожної екшену свого спадкоємця цього класу, в якому визначити метод callback, після чого створити новий об'єкт класу-спадкоємця, передавши в конструктор ім'я екшену:

class Something extends AJAX_Handler {
callback function() {
wp_send_json_success(array('test'=>'Works!'));
}
}

Something new('do_something');

На хук wp_ajax_nopriv_XXX вішається метод
callback_nopriv
, який в батьківському класі просто викликає метод
callback
, але в спадкоємця її, звісно, можна було змінити. В результаті цього при зверненні до урл виду
/wp-admin/admin-ajax.php?action=do_something
отримаємо відповідь виду
{"success":true,"data":{"test":"Works!"}}
.

CORS і можлива біда з протоколом HTTP/HTTPS)
З міркувань безпеки веб-браузери обмежують можливість HTTP-запитів до ресурсів, чий домен відрізняється від домену, на якому запущений виконує ці запити скрипт. CORS («cross-origin resource sharing») це рекомендований W3C механізм, що дозволяє вирішити крос-доменних запити до сервера (з боку сервера). Якщо ви хочете зробити з обробника AJAX-запитів якусь подобу API для доступу зі сторонніх ресурсів (якою б жахливою не здавалася ця ідея, цьому може знайтися застосування) — доведеться або дозволити крос-доменних запити до обробника, додавши заголовок
Access Control-Allow-Origin
, або реалізувати API за допомогою JSONp.

Якщо у вас на сайті налаштована робота в HTTPS функція admin_url поверне посилання https. Але якщо на сайті неправильний/минулий SSL-сертифікат буде відбуватися редірект на http-версію сайту і сайт буде проглядатися по протоколу http. У підсумку фронтенду дістанеться посилання на обробник в іншому протоколі, що, швидше за все, викличе проблеми. Зокрема, браузер Firefox, швидше за все, не "зрадіє" цього, вирішивши, що ви намагаєтеся робити крос-доменний запит.

Тому, якщо ви використовуєте HTTPS, не впевнені, що з сертифікатом завжди і все буде в порядку і хочете бути впевнені, що навіть в разі якщо він відвалиться, сайт буде працювати нормально, має сенс на фронтенде перевіряти, чи відповідає протокол посилання обробника протоколу сторінки, на якій запущено скрипт.

Автор: Ілля Андрієнко, веб-розробник DataArt.

Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.