Как создать ajax-форму в WordPress без плагинов

  1. Главная
  2. Блог
  3. WordPress
  4. Как создать ajax-форму в WordPress без плагинов

Введение

В этой статье мы научимся создавать ajax-форму в WordPress с нуля, без плагинов, с использованием PHP. О внешнем виде думать не будем, сосредоточимся на технической части.

У этой статьи есть продолжение — «Защита ajax-формы WordPress от СПАМа» — там мы защитили форму от СПАМ-ботов при помощи сервисов капчи.

Где писать код

Если не уверены где писать код — то можете писать его в файле functions.php вашей темы. Если у вас скаченная тема — то лучше создайте дочернюю тему и работайте с ней, либо создайте плагин и пишите код в нем. О том, как создать плагин можно почитать в этой статье.

Создание и вывод формы

Напишем HTML-код простой формы:

<form method="POST" data-admin-ajax="<?php echo admin_url( "admin-ajax.php" ); ?>" action="<?php echo $_SERVER['REQUEST_URI']; ?>" id="recall_form">
    <input type="text" name="your-name" value="">
    <input type="email" name="your-email" value="">
    <button>Отправить</button>
</form>

В атрибуте data-admin-ajax укажем ссылку на файл admin-ajax.php. Именно туда следует отправлять все ajax-запросы в WordPress.

Никогда не создавайте дополнительные файлы, в которые будут посылаться ajax-запросы. Такие запросы следует отправлять строго либо в файл admin-ajax.php, либо использовать REST API. Но REST API — тема для отдельной статьи.

Чтобы разместить такую форму на сайте, ее можно либо записать непосредственно в файл шаблона страницы, вывести через хук, либо использовать шорткоды. Остановимся на последнем варианте. Добавим в functions.php такой код:

<?php

/**
 * Функция, которая будет исполняться на месте шорткода
 * Такая функция всегда должна возвращать строку и никогда выводить контент сама. 
 * Т.е. функции типа echo использовать нельзя.
 */
function feodoraxis_recall_form() {
    return '<form method="POST" data-admin-ajax="' . admin_url( "admin-ajax.php" ) . '" action="' . $_SERVER['REQUEST_URI'] . '" id="recall_form">
        <input type="text" name="your-name" value="">
        <input type="email" name="your-email" value="">
        <button>Отправить</button>
    </form>';
}

/**
 * В первом параметре функции указываем название шорткода, а во втором - название функции, которую следует добавить к нему.
 */
add_shortcode( 'feodoraxis_recall_form', 'feodoraxis_recall_form' );

Хотя в комментарии я написал, что функции типа echo использовать нельзя, это не совсем так. Т.к. я предполагаю, что вы начинающий разработчик, то скорее всего не знакомы с функциями кеширования типа ob_start(), которые нужны для буферизации вывода, и с их помощью можно обойти запрет на использование функций типа echo. Но это тема для отдельной статьи, а вам — на заметку.

Теперь, если мы в любом месте в контенте сайта напишем [feodoraxis_recall_form] — то на месте этого шорткода исполнится функция feodoraxis_recall_form().

Создание ajax-запроса

Форма есть, теперь нужно создать и отправить ajax-запрос. Для этого напишем javascript-код и выведем его в подвале сайта. Хотя JS, конечно, лучше писать в отдельном файле и подключать его. Но для академ примера выведем в подвале через хук wp_footer. Добавим в functions.php следующий код:

<?php
function feodoraxis_show_js_for_form() {
    ?>
    <script>
        
        document.addEventListener("DOMContentLoaded", () => { //Исполняем скрипт только когда загрузится страница, заодно оборачиваем скрипт в свое пространство
            const recallForm = document.querySelector('#recall_form');

            //Если формы нет - то дальше ничего не делаем
            if (!recallForm) {
                return;
            }

            recallForm.addEventListener('submit', (event) => {

                //Отменим стандартное поведение формы
                event.preventDefault();

                // Получим данные формы
                const formData = new FormData(recallForm);

                //Обязательно добавляем значение action, которое мы будем использовать для обработки запроса на стороне сервера
                formData.append('action', 'recall_form');

                //Создадим объект запроса
                const request = new XMLHttpRequest();

                //Откроем запрос, определим метод и адрес куда его отправить, из атрибута data-admin-ajax формы
                request.open("POST", recallForm.getAttribute("data-admin-ajax"));
            
                //Отпрвим запрос
                request.send(formData);

                //При получении ответа от сервера обработаем его
                request.onload = () => {

                    //Получим ответ в виде JSON и преобразуем его в объект для удобства
                    const resultJson = request.responseText;
                    const result = JSON.parse(resultJson);

                    //При успешной обработке запроса выведем сообщение сервера
                    if (result.success === true) {
                        alert(result.data.message);
                    } else { //В случае неудачи выведем информацию об ошибке
                        alert(result.data.error);
                    }
                    
                };
            });

        });
    </script>
    <?php
}

add_action( "wp_footer", "feodoraxis_show_js_for_form", 1000 );

Обработка запроса

Запрос отправили, теперь обработаем информацию, которая приходит из формы через скрипт. Добавим следующий код, куда вы думаете? Правильно — в functions.php!

<?php
//Обработка запроса от авторизованных на сайте пользователей
add_action( 'wp_ajax_' . 'recall_form', 'feodoraxis_recall_form_request' );

//Обработка запроса от неавторизованных на сайте пользователей
add_action( 'wp_ajax_nopriv_' . 'recall_form', 'feodoraxis_recall_form_request' );

function feodoraxis_recall_form_request() {

    //Функция должна обрабатываться исключительно по POST-запросу и только в рамках AJAX-запросов
    if ( $_SERVER['REQUEST_METHOD'] != 'POST' || ! wp_doing_ajax() ) {

        //С помощью этой функции можем отправить ошибку и завершить исполнение функции
        wp_send_json_error();
    }

    //Валидация полей формы на backend
    if ( ! isset( $_POST['your-name'] ) || ! isset( $_POST['your-email'] ) ||  empty( trim( $_POST['your-name'] ) ) || empty( trim( $_POST['your-email'] ) ) ) {

        //Внутри функции можно отправлять любые данные. Они будут в параметре data, к которому мы обращаемся из JS
        wp_send_json_error( [
            'error' => "Необходимо заполнить все поля формы"
        ] );
    }

    //Проверим, что пользователь передал email
    if ( ! is_email( $_POST['your-email'] ) ) {
        wp_send_json_error( [
            'error' => "Указан некорректный email"
        ] );
    }

    //Сохраним наши данные. sanitize_text_field очищает строку и делает ее безопасней
    $your_name = sanitize_text_field( $_POST['your-name'] );
    $your_email = sanitize_text_field( $_POST['your-email'] );

    //Укажем заголовки письма
    $headers = "MIME-Version: 1.0\r\n";

    //Сделаем так, чтобы в сообщении письма можно было указывать HTML
    $headers .= "Content-type: text/html; charset=utf-8\r\n";

    //Укажем имя и Email отправителя письма
    $headers .= "From: Андрей Смородин <no-reply@" . $_SERVER["HTTP_HOST"] . ">\r\n";

    //Оформим содержимое письма
    $message = '<b>Имя:</b> ' . $your_name . '<br>';
    $message .= '<b>Email:</b> ' . $your_email . '<br>';

    //Отправляем письмо
    $result = wp_mail( 'info@feodoraxis.ru', 'Заявка из формы обратной связи', $message, $headers )

    //Проверяем, отправилось-ли письмо
    if ( $result ) {

        //Аналогичная функция как wp_send_json_error, но уведомляет об успехе
        wp_send_json_success( [
            'message' => "Сообщение отправлено. Мы свяжемся с вами в ближайшее время."
        ] );
    }

    wp_send_json_error( [
        'error' => "Письмо не было отправлено"
    ] );
}

Важно: в конце функции, которая исполняется по ajax, всегда нужно вызывать функцию exit или die(). Также можно обойтись одной из функций wp_send_json_success() и wp_send_json_error() — они в конце сами вызывают функцию die, но помогают удобно отправлять информацию на сторону клиента: указывают правильные заголовки в ответе сервера, выводят массив в виде json-строки, сообщают об успехе или неудаче, и вызывают в конце функцию die;

Отлично! Если вы все сделали правильно, то форма уже должна работать!

Скоро выйдет вторая часть этой статьи, где мы рассмотрим защиту этой формы от СПАМа, при помощи Yandex Smart Captcha и Google reCaptcha.

Желаю вам успехов!

Листинг всего кода

<?php

/**
 * Функция, которая будет исполняться на месте шорткода
 * Такая функция всегда должна возвращать строку и никогда выводить контент сама. 
 * Т.е. функции типа echo использовать нельзя.
 */
function feodoraxis_recall_form() {
    return '<form method="POST" data-admin-ajax="' . admin_url( "admin-ajax.php" ) . '" action="' . $_SERVER['REQUEST_URI'] . '" id="recall_form">
        <input type="text" name="your-name" value="">
        <input type="email" name="your-email" value="">
        <button>Отправить</button>
    </form>';
}

/**
 * В первом параметре функции указываем название шорткода, а во втором - название функции, которую следует добавить к нему.
 */
add_shortcode( 'feodoraxis_recall_form', 'feodoraxis_recall_form' );

/**
 * Функция для вывода в подвале сайта JavaScript, отправляющего ajax-запрос
 */
function feodoraxis_show_js_for_form() {
    ?>
    <script>
        
        document.addEventListener("DOMContentLoaded", () => { //Исполняем скрипт только когда загрузится страница, заодно оборачиваем скрипт в свое пространство
            const recallForm = document.querySelector('#recall_form');

            //Если формы нет - то дальше ничего не делаем
            if (!recallForm) {
                return;
            }

            recallForm.addEventListener('submit', (event) => {

                //Отменим стандартное поведение формы
                event.preventDefault();

                // Получим данные формы
                const formData = new FormData(recallForm);

                //Обязательно добавляем значение action, которое мы будем использовать для обработки запроса на стороне сервера
                formData.append('action', 'recall_form');

                //Создадим объект запроса
                const request = new XMLHttpRequest();

                //Откроем запрос, определим метод и адрес куда его отправить, из атрибута data-admin-ajax формы
                request.open("POST", recallForm.getAttribute("data-admin-ajax"));
            
                //Отпрвим запрос
                request.send(formData);

                //При получении ответа от сервера обработаем его
                request.onload = () => {

                    //Получим ответ в виде JSON и преобразуем его в объект для удобства
                    const resultJson = request.responseText;
                    const result = JSON.parse(resultJson);

                    //При успешной обработке запроса выведем сообщение сервера
                    if (result.success === true) {
                        alert(result.data.message);
                    } else { //В случае неудачи выведем информацию об ошибке
                        alert(result.data.error);
                    }
                    
                };
            });

        });
    </script>
    <?php
}

add_action( "wp_footer", "feodoraxis_show_js_for_form", 1000 );

//Обработка запроса от авторизованных на сайте пользователей
add_action( 'wp_ajax_' . 'recall_form', 'feodoraxis_recall_form_request' );

//Обработка запроса от неавторизованных на сайте пользователей
add_action( 'wp_ajax_nopriv_' . 'recall_form', 'feodoraxis_recall_form_request' );

//Функция, которая обрабатывает входящий ajax-запрос
function feodoraxis_recall_form_request() {

    //Функция должна обрабатываться исключительно по POST-запросу и только в рамках AJAX-запросов
    if ( $_SERVER['REQUEST_METHOD'] != 'POST' || ! wp_doing_ajax() ) {

        //С помощью этой функции можем отправить ошибку и завершить исполнение функции
        wp_send_json_error();
    }

    //Валидация полей формы на backend
    if ( ! isset( $_POST['your-name'] ) || ! isset( $_POST['your-email'] ) ||  empty( trim( $_POST['your-name'] ) ) || empty( trim( $_POST['your-email'] ) ) ) {

        //Внутри функции можно отправлять любые данные. Они будут в параметре data, к которому мы обращаемся из JS
        wp_send_json_error( [
            'error' => "Необходимо заполнить все поля формы"
        ] );
    }

    //Проверим, что пользователь передал email
    if ( ! is_email( $_POST['your-email'] ) ) {
        wp_send_json_error( [
            'error' => "Указан некорректный email"
        ] );
    }

    //Сохраним наши данные. sanitize_text_field очищает строку и делает ее безопасней
    $your_name = sanitize_text_field( $_POST['your-name'] );
    $your_email = sanitize_text_field( $_POST['your-email'] );

    //Укажем заголовки письма
    $headers = "MIME-Version: 1.0\r\n";

    //Сделаем так, чтобы в сообщении письма можно было указывать HTML
    $headers .= "Content-type: text/html; charset=utf-8\r\n";

    //Укажем имя и Email отправителя письма
    $headers .= "From: Андрей Смородин <no-reply@" . $_SERVER["HTTP_HOST"] . ">\r\n";

    //Оформим содержимое письма
    $message = '<b>Имя:</b> ' . $your_name . '<br>';
    $message .= '<b>Email:</b> ' . $your_email . '<br>';

    //Отправляем письмо
    $result = wp_mail( 'info@feodoraxis.ru', 'Заявка из формы обратной связи', $message, $headers )

    //Проверяем, отправилось-ли письмо
    if ( $result ) {

        //Аналогичная функция как wp_send_json_error, но уведомляет об успехе
        wp_send_json_success( [
            'message' => "Сообщение отправлено. Мы свяжемся с вами в ближайшее время."
        ] );
    }

    wp_send_json_error( [
        'error' => "Письмо не было отправлено"
    ] );
}