Форма обратной связи для сайта

Здравствуйте. Данная статья является ответом на вопрос, который мне задавали несколько человек в последнее время. Звучит вопрос так: «Как сделать контактную форму обратной связи для лендинга без использования сторонних онлайн-сервисов».

Форма обратной связи нужна практически для любого сайта. Если сайт работает на базе какой-то популярной системы управления контентом (WordPress, Joomla, Drupal и т.д.), то для таких CMS существуют различные плагины, установив которые, можно легко решить эту задачу, но лендинг не работает на CMS, в большинстве случаев он состоит из файла index.html и подключаемых к нему CSS-стилей и JS-скриптов.

Исходя из вышесказанного следует, что готовый плагин здесь не подойдет, и придется реализовать собственное решение. В конце статьи будет ссылка для скачивания архива со всеми исходниками, использовавшимися в этом посте.

Создание контактной формы

Сначала нужно определиться с необходимыми полями:

  • Имя отправителя.
  • E-mail адрес.
  • Номер телефона.
  • Текст сообщения.
  • Согласие с обработкой персональных данных.

Валидировать и отправлять форму будем асинхронно без перезагрузки страницы, используя Ajax, чтобы было проще с ним работать, воспользуемся библиотекой jQuery, в подавляющем большинстве случаев она к вашим лендингам уже подключена.

Еще хотелось бы на клиенте реализовать ввод номера телефона по маске, чтобы можно было вводить только цифры в определенном формате. Для это воспользуемся jQuery-плагином maskedinput. Валидация E-mail на клиенте будет производиться встроенными средствами браузера.

Помимо этого нужно создать еще два файла, main.css — для оформления и main.js — для отправки Ajax-запроса на сервер. Еще нам понадобится лоадер (GIF-изображение, которое будет крутиться в момент отправки почты). Все файлы сгруппируем в соответствующих папках (css, js, images). Ну и, конечно, в корне создаем индексный файл index.html и обработчик handler.php, который будет принимать и обрабатывать данные из формы. Получилась следующая структура.

Структура папок и файлов

В index.html помещу HTML-код формы и подключу стили и скрипты:

<!DOCTYPE html>
<html lang="ru">
<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, initial-scale=1">
	<title>Форма обратной связи для сайта</title>
	<link rel="stylesheet" href="css/main.css">
</head>
<body>
	<div class="container">
		<h2>Форма обратной связи</h2>
		<form id="contactForm" action="handler.php" method="post">
			<div class="field-block">
				<label for="name">Ваше имя:</label>
				<input id="name" class="field" name="name" required type="text" placeholder="Иванов Иван Иванович">
			</div>
			<div class="field-block">
				<label for="email">Ваш E-mail:</label>
				<input id="email" class="field" name="email" required type="email" placeholder="ivanov@email.com">
			</div>
			<div class="field-block">
				<label for="phone">Ваш телефон:</label>
				<input id="phone" class="field" name="phone" required type="text" placeholder="+7 (800) 000-00-00">
			</div>
			<div class="field-block">
				<label for="message">Текст сообщения:</label>
				<textarea id="message" class="field" required name="message" rows="4"></textarea>
			</div>
			<div class="field-block">
				<input id="check" name="check" checked type="checkbox">
				<span class="check-text">Я добровольно отправляю свои данные</span>
			</div>
			<button id="button" class="button" type="submit">Отправить</button>
			<div class="result">
				<span id="answer"></span>
				<span id="loader"><img src="images/loader.gif" alt=""></span>
			</div>
		</form>
	</div>
	<script src="js/jquery.min.js"></script>
	<script src="js/jquery.maskedinput.min.js"></script>
	<script src="js/main.js"></script>
</body>
</html>

В файле main.css накидаю простенькие стили для оформления:

* {
	padding: 0;
	margin: 0;
	box-sizing: border-box;
}

body {
	font-family: Arial, sans-serif;
	font-size: 16px;
	background-color: #fff;
}

.container {
	max-width: 500px;
	margin: 50px auto;
	padding: 20px;
	border-radius: 4px;
	background-color: #ebebeb;
	box-shadow: 0 0 3px #666;
}

h2, .field-block {
	margin-bottom: 20px;
}

.field-block label, .field-block .field {
	display: block;
	width: 100%;
}

.field-block label {
	margin-bottom: 8px;
	font-weight: bold;
}

.field-block .field {
	font-size: 16px;
	padding: 8px 12px;
	line-height: 1.5;
	border-radius: 4px;
	border: 1px solid #ccc;
	box-shadow: inset 0 1px 1px rgba(0,0,0,0.075);
	transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;
}

.field-block .field:focus {
  border-color: #66afe9;
  outline: 0;
  box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6);
}

.field-block textarea {
	resize: vertical;
}

.check-text {
	font-size: 14px;
	vertical-align: top;
}

.button {
	cursor: pointer;
	font-size: 16px;
	padding: 6px 12px;
	margin-bottom: 5px;
}

#loader {
	display: none;
}

На реальном сайте вы можете использовать абсолютно любой HTML-код формы с собственными CSS-стилями, которые подходят для дизайна вашей веб-страницы. Мой вариант внешнего вида формы сделан просто для примера.

В результате получилась вот такая форма:

Пример простой контактной формы

Наполняем main.js:

  • Включаем маску для ввода номера телефона.
  • Делаем проверку, если чекбокс согласия на обработку персональных данных не отмечен, то деактивируем кнопку и не даем отправить форму.
  • Формируем Ajax-запрос для отправки пользовательских данных на сервер и получение ответа от сервера.

Код в main.js получился такой:

jQuery(document).ready(function($) {

    // Добавляем маску для поля с номера телефона
    $('#phone').mask('+7 (999) 999-99-99');

    // Проверяет отмечен ли чекбокс согласия
    // с обработкой персональных данных
    $('#check').on('click', function() {
        if ($("#check").prop("checked")) {
            $('#button').attr('disabled', false);
        } else {
            $('#button').attr('disabled', true);
        }
    });

    // Отправляет данные из формы на сервер и получает ответ
    $('#contactForm').on('submit', function(event) {
        
        event.preventDefault();

        var form = $('#contactForm'),
            button = $('#button'),
            answer = $('#answer'),
            loader = $('#loader');

        $.ajax({
            url: 'handler.php',
            type: 'POST',
            data: form.serialize(),
            beforeSend: function() {
                answer.empty();
                button.attr('disabled', true).css('margin-bottom', '20px');
                loader.fadeIn();
            },
            success: function(result) {
                loader.fadeOut(300, function() {
                    answer.text(result);
                });
                form.find('.field').val('');
                button.attr('disabled', false);
            },
            error: function() {
                loader.fadeOut(300, function() {
                    answer.text('Произошла ошибка! Попробуйте позже.');
                });
                button.attr('disabled', false);
            }
        });
    
    });

});

На этом клиентская часть нашей формы завершена, переходим к серверной части.

Отправка почты на E-mail администратора

Для того чтобы отправка почты работала корректно и не было проблем со спам-фильтрами, отправляться письма должны с реального E-mail адреса и через SMTP-серверы популярных почтовых служб (Яндекс, Mail, Google).

Как это работает? В файле на сайте мы указываем логин и пароль от реального ящика, по этим данным перед отправкой почты происходит авторизация в системе почтовика и отправка с этого ящика на другой E-mail адрес (в данном случае E-mail администратора сайта).

Для реализации такого функционала воспользуемся одной из нескольких PHP-библиотек, например: PHPMailer, на мой взгляд она  попроще, и новичку будет легче с ней разобраться. Так как мы имеем дело с лендингом или сайт-визиткой, то у нас нет композера и вряд ли он понадобится, поэтому просто скачиваем и распаковываем архив.

В корне нашего сайта создаем папку mailer и помещаем в нее папку с библиотекой, должно получиться так: site.ru/mailer/PHPMailer, на реальном сервере желательно запретить доступ к папке mailer.

Чтобы не пихать весь код в пока пустой ранее созданный обработчик handler.php, в папке mailer создадим два вспомогательных статических хелпера: Validator.php (валидирует данные из формы) и ContactMailer.php (отправляет почту, обертка над PHPMailer).

Код из Validator.php:

<?php
/**
 * Validator: класс-хелпер, валидацирует данные пришедшие от пользователя
 */
class Validator
{
	/**
     * Проверяет валидный ли E-mail, если да, то
     * возвращает TRUE, в противном случае FALSE.
     * @param string $email
     * @return boolean
     */
	public static function isValidEmail($email)
	{
		return filter_var($email, FILTER_VALIDATE_EMAIL);
	}

	/**
     * Проверяет валидный ли E-mail, если да, то
     * возвращает TRUE, в противном случае FALSE.
     * @param string $phone
     * @return boolean
     */
	public static function isValidPhone($phone)
	{
		return preg_match("#\+7 \(\d{3}\) \d{3}\-\d{2}-\d{2}#", $phone);
	}

	/**
     * Проверяет пришли ли данные POST-запросом, если да,
     * то возвращает TRUE, в противном случае FALSE.
     * @return boolean
     */
	public static function isPost()
	{
		return $_SERVER['REQUEST_METHOD'] === 'POST';
	}

	/**
     * Проверяет пришли ли данные Ajax-запросом, если да,
     * то возвращает TRUE, в противном случае FALSE.
     * @return boolean
     */
	public static function isAjax()
	{
		return isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] === 'XMLHttpRequest';
	}
}

Код из ContactMailer.php:

<?php

use PHPMailer\PHPMailer\Exception;
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\SMTP;

require_once __DIR__ . '/PHPMailer/src/Exception.php';
require_once __DIR__ . '/PHPMailer/src/PHPMailer.php';
require_once __DIR__ . '/PHPMailer/src/SMTP.php';
/**
 * Mailer: класс-хелпер, отправляет почту администратору
 */
class ContactMailer
{
	/**
     * E-mail отправителя
     * @var string
     */
    private static $emailFrom = 'send@yandex.ru';
    /**
     * E-mail получателя
     * @var string
     */
    private static $emailTo = 'admin@yandex.ru';

    /**
     * Отправляет писмо, если письмо отправлено,
     * возвращает TRUE, в противном случае FALSE.
     * @param string $name
     * @param string $email
     * @param string $phone
     * @param string $message
     * @return boolean
     */
    public static function send($name, $email, $phone, $message)
    {
		    // Формируем тело письма
		    $body = "Имя: " . $name . "\nE-mail: " . $email . "\nТелефон: " . $phone . "\n\nСообщение:\n" . $message;

		    // Создаем объект PHPMailer
        $mailer = new PHPMailer();
        // Настройки подключения
        $mailer->isSMTP();
        // Устанавливает хост почтового сервера (Mail.ru: smtp.mail.ru, Google: smtp.gmail.com)
        $mailer->Host = ' smtp.yandex.ru';
        // Включает SMTP-авторизацию
        $mailer->SMTPAuth = true;
        // Логин или E-mail целиком
        $mailer->Username = self::$emailFrom;
        // Пароль от почтового ящика
        $mailer->Password = 'XXXXXXXX';
        // Протокол соединения
        $mailer->SMTPSecure = 'ssl';
        // Порт для исходящаей почты
        $mailer->Port = '465';

        // Устанавливает кодировку
        $mailer->CharSet = 'UTF-8';
        // Устанавливает E-mail и имя отправителя
        $mailer->setFrom(self::$emailFrom, 'Имя отправителя');
        // Добавляет E-mail получателя
        $mailer->addAddress(self::$emailTo);
        // Настройка HTML-формата
        $mailer->isHTML(false);
        // Тема письма
        $mailer->Subject = 'Заполнена форма обратной связи';
        // Основное тело письма
        $mailer->Body = $body;
        
        // Отправляет письмо
        if ($mailer->send()) {
        	return true;
        }
    	  return false;
    }
}

Осталось лишь сформировать наш обработчик handler.php:

<?php

require_once __DIR__ . '/mailer/Validator.php';
require_once __DIR__ . '/mailer/ContactMailer.php';

if (!Validator::isAjax() || !Validator::isPost()) {
	echo 'Доступ запрещен!';
	exit;
}

$name = isset($_POST['name']) ? trim(strip_tags($_POST['name'])) : null;
$email = isset($_POST['email']) ? trim(strip_tags($_POST['email'])) : null;
$phone = isset($_POST['phone']) ? trim(strip_tags($_POST['phone'])) : null;
$message = isset($_POST['message']) ? trim(strip_tags($_POST['message'])) : null;

if (empty($name) || empty($email) || empty($phone) || empty($message)) {
	echo 'Все поля обязательны для заполнения.';
	exit;
}

if (!Validator::isValidEmail($email)) {
	echo 'E-mail не соответствует формату.';
	exit;
}

if (!Validator::isValidPhone($phone)) {
	echo 'Телефон не соответствует формату.';
	exit;
}

if (ContactMailer::send($name, $email, $phone, $message)) {
	echo htmlspecialchars($name) . ', ваше сообщение успешно отправлено.';
} else {
	echo 'Произошла ошибка! Не удалось отправить сообщение.';
}
exit;

Обратите внимание, что телефон на сервере валидируется по такой же маске, как и на клиенте. Также проверяется тип запроса, пришедшего на сервер, E-mail — на валидность, и все поля — на пустоту. Если не сработает хоть одно условие, то форма не отправится и выведется сообщение об соответствующей ошибке.

Не забудьте изменить данные в настройках ContactMailer.php: E-mail получателя, E-mail и пароль отправителя, адрес вашего SMTP-сервера и т.д. Ко всем используемым настройкам я написал пояснение в коде. Если будите применять данное решение на реальном сайте, внимательно смотрите все пути к файлам и сделайте бекап, прежде чем вносить изменения.

Библиотека PHPMailer предоставляет гораздо больше возможностей, я использовал лишь необходимые для конкретной задачи. Все возможности смотрите в документации на GitHab, ссылку я дал выше.

На этом все. Надеюсь, все понятно изложил. Если остались вопросы, пишите в комментариях. В следующей статье рассмотрим как реализовать форму обратной связи во всплывающем модальном окне, используя Bootstrap 4.

Похожие записи: Axios: библиотека для работы с Ajax без использования jQuery Всплывающая форма обратной связи используя Bootstrap 4 Open Server: установка и работа с сервером

Добавить комментарийОтменить ответ

Нажимая на кнопку «Добавить», я даю согласие на обработку своих персональных данных в соответствии с политикой конфиденциальности