WordPress. Обработка POST-запросов. Часть 2
Плагин уже работает, но он еще далек от совершенства. Давайте доведем его до ума. Во-первых — напишем js-скрипт, который будет отправлять данные формы с использованием объекта XmlHttpRequest. Во-вторых — добавим простенькую защиту от роботов. Но перед этим оформим нашу форму с помощью стилей.
/* * Подключаем файл стилей, чтобы красиво оформить нашу форму */ add_action('wp_print_styles', function () { wp_enqueue_style( 'tokmakov-feedback-css', plugin_dir_url(__FILE__) . 'style.css' ); });
.tokmakov-feedback {
padding: 10px;
margin: 10px 0;
background-color: #f5f5f5;
border: 1px solid #e3e3e3;
}
.tokmakov-feedback label {
display: block;
font-weight: normal;
}
.tokmakov-feedback span {
display: block;
}
.tokmakov-feedback input[type="text"] {
width: 50%;
}
.tokmakov-feedback textarea {
height: 10rem;
display: block;
width: 100%;
}
.tokmakov-feedback label img {
vertical-align: bottom;
}
.tokmakov-feedback input[type="submit"] {
margin-top: 1rem;
}
.tokmakov-feedback p, .tokmakov-feedback ul {
margin-top: 0;
}
Как отправлять AJAX-запросы
Кодекс WordPress говорит, что AJAX-запросы нужно отправлять на admin-ajax.php
в директории wp-admin
. А в отправляемых данных должно быть поле action
. Посмотрим на исходный код этого скрипта (только то, что нам интересно):
<?php define('DOING_AJAX', true); if (!defined('WP_ADMIN')) { define('WP_ADMIN', true); } // загрузка ядра WordPress require_once(dirname(dirname(__FILE__)) . '/wp-load.php'); // без action работать не будет if (empty($_REQUEST['action'])) { wp_die('0', 400); } require_once(ABSPATH . 'wp-admin/includes/admin.php'); do_action('admin_init'); if (is_user_logged_in()) { // если пользователь авторизован do_action('wp_ajax_' . $_REQUEST['action']); } else { // если пользователь не авторизован do_action('wp_ajax_nopriv_' . $_REQUEST['action']); } wp_die('0');
Все очень просто:
- когда пользователь авторизован, происходит событие
wp_ajax_{action}
- когда пользователь не авторизован, происходит событие
wp_ajax_nopriv_{action}
Так что обработать данные формы, когда они отправлются с использованием AJAX, мы можем так:
/* * Обработка данных формы при использовании XmlHttpRequest (AJAX) */ if (wp_doing_ajax()) { add_action('wp_ajax_tokmakov_feedback', 'tokmakov_process_feedback_form'); add_action('wp_ajax_nopriv_tokmakov_feedback', 'tokmakov_process_feedback_form'); // когда пришел ajax-запрос c данными формы, нам больше ничего // делать не надо, так что прерываем работу плагина и выходим return; }
Обратите внимание, что при обработке AJAX-запроса нам не нужно подключать css и js файлы, регистрировать шорткод, стартовать сессию. А вот функцию, которая обрабатывает обычный POST-запрос или AJAX-запрос, мы доработаем.
Отправляем и обрабатываем AJAX-запрос
Хорошо, давайте теперь напишем js-код, который будет отправлять AJAX-запрос с данными формы:
jQuery(document).ready(function($) { /* * Скрываем блок, где будут результаты, чтобы потом красиво показать */ $('.tokmakov-feedback .response').hide(); /* * Добавляем loader, который будем показывать в момент отправки */ var attr = {'src': tokmakov_feedback.loader, 'class': 'loader'}; var $image = $('<img>', attr).hide(); $('.tokmakov-feedback form').append($image); /* * Подписываемся на событие submit, отменяем обычную отправку формы */ $('.tokmakov-feedback form').on('submit', function(e) { // отменяем обычную отправку формы e.preventDefault(); // сама форма, блок с ответом, loader и кнопка отправки var $form = $(this), $response = $form.siblings('.response'), $loader = $form.children('.loader'), $submit = $form.children('input[type=submit]'); // блокируем кнопку submit и показываем loader $submit.prop('disabled', true); $loader.show(); // передаем форму не объектом jQuery, а как DOM-элемент var data = new FormData($form[0]); // отправляем данные формы $.ajax({ url: tokmakov_feedback.handler, type: $form.attr('method'), data: data, contentType: false, // убираем форматирование данных по умолчанию processData: false, // убираем преобразование строк по умолчанию dataType: 'json', success: function(response) { // разблокируем кнопку submit и скрываем loader $submit.prop('disabled', false); $loader.hide(); // создаем абзац текста с ответом сервера $('<p>').text(response.message).appendTo($response); // если были ошибки, создаем список ошибок if (response.errors) { var $errors = $('<ul>').appendTo($response); response.errors.forEach(function(item) { $('<li>').text(item).appendTo($errors); }); } // показываем ответ и ошибки с использованмием анимации $response .slideDown(500) .delay(2000) .slideUp(500, function () { $(this).empty(); // если сообщение отправлено, очищаем форму if (response.success) { $form[0].reset(); } }); } }); }); });
И внесем изменения в php-код плагина:
<?php /* Plugin Name: Форма обратной связи Plugin URI: https://tokmakov.msk.ru Description: Регистрирует шорткод [tokmakov-feedback] для формы обратной связи. Version: 1.0 Author: Евгений Токмаков Author URI: https://tokmakov.msk.ru */ register_activation_hook(__FILE__, function() { // проверяем права пользователя на активацию плагинов if (!current_user_can('activate_plugins')) { return; } }); register_deactivation_hook(__FILE__, function() { // проверяем права пользователя на деактивацию плагинов if (!current_user_can('deactivate_plugins')) { return; } }); /* * Обработка данных формы при использовании XmlHttpRequest (AJAX) */ if (wp_doing_ajax()) { add_action('wp_ajax_tokmakov_feedback', 'tokmakov_process_feedback_form'); add_action('wp_ajax_nopriv_tokmakov_feedback', 'tokmakov_process_feedback_form'); // когда пришел ajax-запрос c данными формы, нам больше ничего // делать не надо, так что прерываем работу плагина и выходим return; } /* * Запускаем сессию, чтобы передавать сообщение о результате * обработки формы обратной связи после перезагрузки страницы */ add_action('init', function () { if (session_id() == '') { session_start(); } }); /* * Регистрируем шорткод [tokmakov-feedback], который позволит * вставить форму загрузки файлов на страницу или запись блога */ add_shortcode('tokmakov-feedback', function () { $name = ''; $email = ''; $phone = ''; $message = ''; if (isset($_SESSION['tokmakov_feedback']['data'])) { $name = htmlspecialchars($_SESSION['tokmakov_feedback']['data']['name']); $email = htmlspecialchars($_SESSION['tokmakov_feedback']['data']['email']); $phone = htmlspecialchars($_SESSION['tokmakov_feedback']['data']['phone']); $message = htmlspecialchars($_SESSION['tokmakov_feedback']['data']['message']); } ob_start(); ?> <div class="tokmakov-feedback"> <div class="response"> <?php if (isset($_SESSION['tokmakov_feedback'])): ?> <p><?= $_SESSION['tokmakov_feedback']['message']; ?></p> <?php if (isset($_SESSION['tokmakov_feedback']['errors'])): ?> <ul> <?php foreach ($_SESSION['tokmakov_feedback']['errors'] as $error): ?> <li><?= $error; ?></li> <?php endforeach; ?> </ul> <?php endif; ?> <?php unset($_SESSION['tokmakov_feedback']); ?> <?php endif; ?> </div> <form action="<?= admin_url('admin-post.php'); ?>" method="post"> <input type="hidden" name="action" value="tokmakov_feedback" /> <input type="hidden" name="redirect" value="<?= get_permalink(); ?>" /> <label> <span>Имя</span> <input type="text" name="name" value="<?= $name; ?>" /> </label> <label> <span>E-mail</span> <input type="text" name="email" value="<?= $email; ?>" /> </label> <label> <span>Телефон</span> <input type="text" name="phone" value="<?= $phone; ?>" /> </label> <label> <span>Сообщение</span> <textarea name="message"><?= $message; ?></textarea> </label> <input type="submit" value="Отправить" /> </form> </div> <?php return ob_get_clean(); }); /* * Подключаем js и css файлы, необходимые для работы плагина */ add_action('wp_enqueue_scripts', function () { wp_enqueue_script( 'tokmakov-feedback-js', plugin_dir_url(__FILE__) . 'script.js', ['jquery'], null, true ); // определяем глобальную js-переменную, которая будет // содержать URL, куда отправлять ajax-запросы wp_localize_script( 'tokmakov-feedback-js', 'tokmakov_feedback', [ 'handler' => admin_url('admin-ajax.php'), 'loader' => plugin_dir_url(__FILE__) . 'loader.gif' ] ); wp_enqueue_style( 'tokmakov-feedback-css', plugin_dir_url(__FILE__) . 'style.css' ); }); /* * Обрабатываем отправленные данные формы обратной связи */ add_action('admin_post_nopriv_tokmakov_feedback', 'tokmakov_process_feedback_form'); add_action('admin_post_tokmakov_feedback', 'tokmakov_process_feedback_form'); function tokmakov_process_feedback_form() { /* * Обрабатываем данные, полученные из формы */ $data['name'] = trim(iconv_substr(strip_tags($_POST['name']), 0, 50)); $data['email'] = trim(iconv_substr(strip_tags($_POST['email']), 0, 50)); $data['phone'] = trim(iconv_substr(strip_tags($_POST['phone']), 0, 50)); $data['message'] = trim(iconv_substr(strip_tags($_POST['message']), 0, 1000)); // были допущены ошибки при заполнении формы? if (empty($data['name'])) { $errors[] = 'Не заполнено обязательное поле «Имя»'; } if (empty($data['email'])) { $errors[] = 'Не заполнено обязательное поле «E-mail»'; } if (empty($data['message'])) { $errors[] = 'Не заполнено обязательное поле «Сообщение»'; } if (!empty($errors)) { /* * были допущены ошибки при заполнении формы, сохраняем введенные * пользователем данные, чтобы после редиректа снова показать форму, * заполненную введенными ранее даннными и сообщением об ошибке */ $response['success'] = false; $message = 'При заполнении формы были допущены ошибки'; $response['message'] = $message; $response['errors'] = $errors; $response['data'] = $data; } else { // отправляем письмо администратору if (tokmakov_sendmail($data)) { $response['success'] = true; $message = 'Ваше сообщение успешно отправлено'; $response['message'] = $message; } else { $response['success'] = false; $message = 'Произошла ошибка при отправке письма'; $response['message'] = $message; $response['data'] = $data; } } if (!wp_doing_ajax()) { $_SESSION['tokmakov_feedback'] = $response; $redirect = home_url(); if (isset($_POST['redirect'])) { $redirect = $_POST['redirect']; $redirect = wp_validate_redirect($redirect, home_url()); } wp_redirect($redirect); die(); } else { wp_send_json($response); } } function tokmakov_sendmail($data) { $message = 'Имя: ' . $data['name'] . PHP_EOL; $message .= 'E-mail: ' . $data['email'] . PHP_EOL; if (!empty($data['phone'])) { $message .= 'Телефон: ' . $data['phone'] . PHP_EOL; } $message .= PHP_EOL . 'Сообщение: ' . PHP_EOL . $data['message'] . PHP_EOL; $result = wp_mail( get_bloginfo('admin_email'), 'Заполнена форма обратной связи', $message ); return $result; }
Добавляем защиту от роботов
Добавим в форму еще одно поле, в которое надо будет ввести код с картинки. Это будет случайное число в интервале от 1000 до 9999, которое мы сохраним в сессию. А при обработке данных будем сравнивать код из формы с кодом из сессии. Для начала напишем скрипт, который будет формировать картинку с кодом:
<?php session_start(); // создаем случайное число и сохраняем в сессии $number = rand(1000, 9999); $_SESSION['tokmakov_feedback_captcha'] = $number; // создаем изображение $img = imagecreatetruecolor(105, 40); // создаем три цвета: для числа, для тени и для фона $white = imagecolorallocate($img, 250, 250, 250); $grey = imagecolorallocate($img, 150, 150, 150); $black = imagecolorallocate($img, 50, 50, 50); imagefilledrectangle($img, 0, 0, 105, 40, $black); // рисуем созданное случайное число imagettftext($img, 30, 0, 10, 30, $grey, 'font.ttf', rand(1000, 9999)); imagettftext($img, 30, 0, 0, 35, $white, 'font.ttf', $number); // предотвращаем кеширование браузером header('Cache-Control: no-store, no-cache, must-revalidate'); header('Expires: ' . date('r')); // отправляем изображение браузеру header ('Content-type: image/gif'); imagegif($img); imagedestroy($img);
И доработаем php-код плагина:
<?php /* Plugin Name: Форма обратной связи Plugin URI: https://tokmakov.msk.ru Description: Регистрирует шорткод [tokmakov-feedback] для формы обратной связи. Version: 1.0 Author: Евгений Токмаков Author URI: https://tokmakov.msk.ru */ register_activation_hook(__FILE__, function() { // проверяем права пользователя на установку плагинов if (!current_user_can('activate_plugins')) { return; } }); register_deactivation_hook(__FILE__, function() { // проверяем права пользователя на деактивацию плагинов if (!current_user_can('deactivate_plugins')) { return; } }); /* * Обработка данных формы при использовании XmlHttpRequest (AJAX) */ if (wp_doing_ajax()) { // сессия нужна для передачи защитного кода if (session_id() == '') { session_start(); } add_action('wp_ajax_tokmakov_feedback', 'tokmakov_process_feedback_form'); add_action('wp_ajax_nopriv_tokmakov_feedback', 'tokmakov_process_feedback_form'); // когда пришел ajax-запрос c данными формы, нам больше ничего // делать не надо, так что прерываем работу плагина и выходим return; } /* * Запускаем сессию, чтобы передавать сообщение о результате * обработки формы обратной связи после перезагрузки страницы */ add_action('init', function () { if (session_id() == '') { session_start(); } }); /* * Регистрируем шорткод [tokmakov-feedback], который позволит * вставить форму загрузки файлов на страницу или запись блога */ add_shortcode('tokmakov-feedback', function () { $name = ''; $email = ''; $phone = ''; $message = ''; if (isset($_SESSION['tokmakov_feedback']['data'])) { $name = htmlspecialchars($_SESSION['tokmakov_feedback']['data']['name']); $email = htmlspecialchars($_SESSION['tokmakov_feedback']['data']['email']); $phone = htmlspecialchars($_SESSION['tokmakov_feedback']['data']['phone']); $message = htmlspecialchars($_SESSION['tokmakov_feedback']['data']['message']); } ob_start(); ?> <div class="tokmakov-feedback"> <div class="response"> <?php if (isset($_SESSION['tokmakov_feedback'])): ?> <p><?= $_SESSION['tokmakov_feedback']['message']; ?></p> <?php if (isset($_SESSION['tokmakov_feedback']['errors'])): ?> <ul> <?php foreach ($_SESSION['tokmakov_feedback']['errors'] as $error): ?> <li><?= $error; ?></li> <?php endforeach; ?> </ul> <?php endif; ?> <?php unset($_SESSION['tokmakov_feedback']); ?> <?php endif; ?> </div> <form action="<?= admin_url('admin-post.php'); ?>" method="post"> <input type="hidden" name="action" value="tokmakov_feedback" /> <input type="hidden" name="redirect" value="<?= get_permalink(); ?>" /> <label> <span>Имя</span> <input type="text" name="name" value="<?= $name; ?>" /> </label> <label> <span>E-mail</span> <input type="text" name="email" value="<?= $email; ?>" /> </label> <label> <span>Телефон</span> <input type="text" name="phone" value="<?= $phone; ?>" /> </label> <label> <span>Сообщение</span> <textarea name="message"><?= $message; ?></textarea> </label> <label> <span>Защитный код</span> <input type="text" name="captcha" value="" /> <img src="<?= plugin_dir_url(__FILE__) ?>captcha.php" alt="" /> </label> <input type="submit" value="Отправить" /> </form> </div> <?php return ob_get_clean(); }); /* * Подключаем js и css файлы, необходимые для работы плагина */ add_action('wp_enqueue_scripts', function () { wp_enqueue_script( 'tokmakov-feedback-js', plugin_dir_url(__FILE__) . 'script.js', ['jquery'], null, true ); // определяем глобальную js-переменную, которая будет // содержать URL, куда отправлять ajax-запросы wp_localize_script( 'tokmakov-feedback-js', 'tokmakov_feedback', [ 'handler' => admin_url('admin-ajax.php'), 'loader' => plugin_dir_url(__FILE__) . 'loader.gif' ] ); wp_enqueue_style( 'tokmakov-feedback-css', plugin_dir_url(__FILE__) . 'style.css' ); }); /* * Обрабатываем отправленные данные формы обратной связи */ add_action('admin_post_nopriv_tokmakov_feedback', 'tokmakov_process_feedback_form'); add_action('admin_post_tokmakov_feedback', 'tokmakov_process_feedback_form'); function tokmakov_process_feedback_form() { /* * Обрабатываем данные, полученные из формы */ $data['name'] = trim(iconv_substr(strip_tags($_POST['name']), 0, 50)); $data['email'] = trim(iconv_substr(strip_tags($_POST['email']), 0, 50)); $data['phone'] = trim(iconv_substr(strip_tags($_POST['phone']), 0, 50)); $data['message'] = trim(iconv_substr(strip_tags($_POST['message']), 0, 1000)); $data['captcha'] = trim(iconv_substr(strip_tags($_POST['captcha']), 0, 4)); // были допущены ошибки при заполнении формы? if (empty($data['name'])) { $errors[] = 'Не заполнено обязательное поле «Имя»'; } if (empty($data['email'])) { $errors[] = 'Не заполнено обязательное поле «E-mail»'; } if (empty($data['message'])) { $errors[] = 'Не заполнено обязательное поле «Сообщение»'; } if (!empty($data['captcha'])) { if ($data['captcha'] != $_SESSION['tokmakov_feedback_captcha']) { $errors[] = 'Не совпадает защитный код с картинки'; } } else { $errors[] = 'Не заполнено обязательное поле «Защитный код»'; } if (!empty($errors)) { /* * были допущены ошибки при заполнении формы, сохраняем введенные * пользователем данные, чтобы после редиректа снова показать форму, * заполненную введенными ранее даннными и сообщением об ошибке */ $response['success'] = false; $message = 'При заполнении формы были допущены ошибки'; $response['message'] = $message; $response['errors'] = $errors; $response['data'] = $data; } else { // отправляем письмо администратору if (tokmakov_sendmail($data)) { $response['success'] = true; $message = 'Ваше сообщение успешно отправлено'; $response['message'] = $message; } else { $response['success'] = false; $message = 'Произошла ошибка при отправке письма'; $response['message'] = $message; $response['data'] = $data; } } if (!wp_doing_ajax()) { $_SESSION['tokmakov_feedback'] = $response; $redirect = home_url(); if (isset($_POST['redirect'])) { $redirect = $_POST['redirect']; $redirect = wp_validate_redirect($redirect, home_url()); } wp_redirect($redirect); die(); } else { wp_send_json($response); } } function tokmakov_sendmail($data) { $message = 'Имя: ' . $data['name'] . PHP_EOL; $message .= 'E-mail: ' . $data['email'] . PHP_EOL; if (!empty($data['phone'])) { $message .= 'Телефон: ' . $data['phone'] . PHP_EOL; } $message .= PHP_EOL . 'Сообщение: ' . PHP_EOL . $data['message'] . PHP_EOL; $result = wp_mail( get_bloginfo('admin_email'), 'Заполнена форма обратной связи', $message ); return $result; }
Скачать плагин «Форма обратной связи» можно здесь.
- WordPress. Обработка POST-запросов. Часть 1
- WordPress. Загрузка файлов. Часть 3 из 3
- WordPress. Защита формы комментария
- WordPress. Форма комментирования
- WordPress. Фильтр записей по произвольным полям. Часть 3 из 3
- WordPress. Фильтр записей по произвольным полям. Часть 2 из 3
- WordPress. Порядок загрузки страницы
Поиск: AJAX • JSON • POST • Web-разработка • Форма • CMS • Событие • Hook • Обратная связь • Плагин • Plugin • Feedback