Laravel AJAX: создаём форму обратной связи

Автор: | 28.08.2017

laravel-ajaxДоброго времени суток!

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

Т.е. то, чем занимаются большинство веб-студий, требуя за свои услуги от 1000$. Если же вы наберётесь терпения изучить весь курс, то сэкономите означенную сумму и время на объяснение деталей сторонним исполнителям :-)

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

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

Сегодня же мы используем полученный опыт и приступим к созданию формы обратной связи, на примере которой я покажу вам, как реализовать популярную технологию AJAX в рамках Laravel framework.

Забегая наперёд, скажу, что саму отправку email через форму мы сегодня делать не будем, т.к. для обзора способов отправки email в Laravel запланирована отдельная публикация, а всего лишь подготовим всё необходимое для этого.

Итак, чем же мы сегодня конкретно займёмся?

Laravel AJAX форма — постановка задачи

Сказав, что сегодня мы будем делать обратную форму на Laravel, я немного слукавил, т.к. она у нас уже есть и доступна по url — laravel.portfolio/contact (я решил его немного поменять, поэтому в дальнейшем форма будет доступна по url laravel.portfolio/contacts).

Единственное, чего ей сейчас не хватает — это функциональности, т.е. непосредственно отправка письма не работает и при нажатии на кнопку «Отправить» фреймворк нам выдаёт ошибку.

Что же тогда мы будет творить, если самой отправкой письма заниматься будем в следующей статье?

Суть нашей сегодняшней работы будет заключаться в написании функции, которая будет вызываться при нажатии на кнопку «Отправить» и будет оправлять данные, введённые пользователем в поля формы, на сервер, а затем, при получении ответа с сервера будет выдавать уведомление об успешной или неуспешной отправке сообщения.

И ещё один нюанс — всё это будет происходить без перезагрузки страницы, т.е. с использованием технологии AJAX.

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

К тому же, AJAX нам понадобится в дальнейшем при работе с Laravel, а при его реализации часто случаются неприятные сюрпризы (у меня их тоже было полно, когда я только начинал работать с этим фреймворком), поэтому я и решил рассмотреть все «подводные камни» сейчас, чтобы не отвлекаться на них потом.

О них мы сейчас и поговорим.

Особенности реализации AJAX в Laravel

AJAX-запросы, по сути, ничем не отличаются от обычных HTTP-запросов к серверу, т.е. они могут отправляться теми же методами (GET, POST, PUT, DELETE и т.д.) с теми же самыми заголовками (HEADERS).

Исторически сложилось, что самыми популярными методами передачи запросов являются GET и POST. В GET-запросах параметры передаются через URL, а при POST они доступны только из суперглобальных массивов $_POST или $_REQUEST.

По этим же причинам метод POST считается более безопасным для передачи конфиденциальных данных, т.к. параметры GET-запроса и их значения можно легко увидеть в браузере.

Исходя из этих соображений, при отправке email в Laravel с помощью AJAX мы будем пользоваться POST-запросом. И тут нас ждёт один неприятный сюрприз, т.к. по умолчанию все AJAX-запросы, отправляемые POST-методом, будут приводить к ошибке:

laravel-oshibka-proverki-csrf-tokena

Что за чушь? Что ещё за VerifyCsrfToken.php, если мы к данному файлу непосредственно не обращаемся?

Если у вас возникли подобные вопросы, то ответы ждут вас в статье о Laravel CSRF.

После её прочтения вы сами поймёте, что нужно сделать, чтобы AJAX POST запрос в Laravel заработал. Если же вопрос всё равно останется открытым, то ответ на него вы получите далее.

Реализация в Laravel AJAX контактной формы

Итак, нам нужно будет сделать следующее:

  • Подготовить HTML-форму для AJAX-отправки запроса
  • Реализовать JavaScript-код для отправки и обработки результатов запроса
  • Прописать правило роутинга Laravel
  • Метод контроллера Laravel для совершения действий на сервере (в будущем именно там у нас будет размещаться код отправки email)

Ну, что такое AJAX и в чём заключается его суть, я думаю, нет смысла описывать, т.к. технология уже лет 10 как не инновационная :-) Если вдруг вам нужны будут подробности — напишите об этом в комментариях.

Поехали :-)

AJAX контактная форма в Laravel — готовим HTML

Итак, что же конкретно мы будем делать?

На данный момент на сайте у нас уже есть контактная форма, которая отправляет POST-запрос на сервер скрипту contact.php. Загляните в файл с кодом контактной формы /resources/views/contact.blade.php, где это прописано:


<form id="contactform" action="contact/contact.php" method="post" class="validateform" name="send-contact">
...
</form>

Кстати, немного подумав, решил переименовать данный файл и соответствующую ссылку на страницу с контактной формой, так что в дальнейшем файл будет называться contacts.blade.php, чтобы было созвучно с новым url /contacts.

Как видите, в шаблоне, который мы используем для создания сайта, всё уже сделано до нас — и скрипт отправки письма, и валидация данных формы (проверка корректности ввода). Но мы от этого избавимся и изобретём собственный велосипед, чтобы было понятно, как он устроен :-)

Кроме того, данные фичи есть не во всех щаблонах, поэтому нужно уметь реализовывать подобное самостоятельно.

Поэтому очищаем тэг формы от ненужных атрибутов. И, чтобы избавится от валидации данных формы, встроенной в шаблон, очистим поля формы от ненужных атрибутов. Стандартной Bootstrap валидации нам вполне хватит.

Ну, и для добавления реализации Laravel CSRF protection и для исключения ошибки проверки CSRF token, о которой мы говорили ранее, в нашу форму следует добавить следующее:


{{ csrf_field() }}

Или воспользоваться другими вариантами указания токена, которые были перечислены в статье о Laravel CSRF по ссылке выше.

В итоге наша форма примет следующий вид:


<form id="contactform" method="POST" class="validateform">
    {{ csrf_field() }}

    <div id="sendmessage">
        Ваше сообщение отправлено!
    </div>
    <div id="senderror">
        При отправке сообщения произошла ошибка. Продублируйте его, пожалуйста, на почту администратора <span>{{ env('MAIL_ADMIN_EMAIL') }}</span>
    </div>
    <div class="row">
        <div class="col-lg-4 field">
            <input type="text" name="name" placeholder="* Введите ваше имя" required />
        </div>
        <div class="col-lg-4 field">
            <input type="email" name="email" placeholder="* Введите ваш email" required />
        </div>
        <div class="col-lg-4 field">
            <input type="text" name="subject" placeholder="* Введите тему сообщения" required />
        </div>
        <div class="col-lg-12 margintop10 field">
            <textarea rows="12" name="message" class="input-block-level" placeholder="* Ваше сообщение..." required></textarea>
            <p>
                <button class="btn btn-theme margintop10 pull-left" type="submit">Отправить</button>
                <span class="pull-right margintop20">* Заполните, пожалуйста, все обязательные поля!</span>
            </p>
        </div>
    </div>
</form>

Как видите, я ещё решил добавить блок с сообщением о неудачной отправке (такое тоже случается) с просьбой связаться с администратором по email напрямую. Email администратора я записал в файл окружения .env, который лежит в корне проекта, в следующем виде:

MAIL_ADMIN_EMAIL=admin@gmail.com

Дописать я его решил в конце секции переменных окружения с приставкой MAIL, поэтому произведите данные действия вслед за мной, т.к. файл .env не находится под контролем версий, следовательно, в репозитории проекта вы его изменений не увидите и будете удивляться, почему что-то не работает :-)

Ну, а для вывода переменных окружения в blade я воспользовался следующей конструкцией:

{{ env('MAIL_ADMIN_EMAIL') }}

Как видите, я воспользовался функцией-хэлпером Laravel env(), которая в качестве аргумента принимает имя переменной окружения, а возвращает, следовательно, её значение. Вызывать её можно как в файлах шаблонизатора, так и из php-кода контроллеров и моделей.

Более подробно о ней вы можете прочитать здесь — https://laravel.com/docs/5.4/helpers#method-env. И вообще рекомендую распечатать данную статью документации Laravel и повесить себе на стеночку, т.к. Laravel helpers — вещь очень удобная и позволяет иногда сэкономить время на написании велосипедов.

Ну, и для визуального оформления сообщения об ошибке отправки письма с контактной формы я добавил стили для селектора #senderror в файлик /public/css/style.css следующим образом:

#sendmessage, #senderror {
	border:1px solid #e6e6e6;
	background:#f6f6f6;
	display:none;
	text-align:center;
	padding:15px 12px 15px 65px;
	margin:10px 0;
	font-weight:600;
	margin-bottom:30px;
}

#senderror {
    color: #f00;
}

#senderror span {
    font-weight: bold;
}

Только не нужно бездумно копипастить код. Первый блок свойств уже существует для селектора #sendmessage. Всё, что нужно, — это добавить эти свойства для #senderror, прописав его через запятую. Остальные два блока просто копируем.

Итак, на этом HTML часть контактной формы готова. Следующим этапом мы подготовим JS-код для передачи запроса и обработки его результатов без перезагрузки страницы.

Laravel AJAX контактная форма — JavaScript код

Поскольку AJAX — это JavaScript технология, то вся магия, как раз и будет происходить здесь. Под магией подразумевается отправка запроса и реакция при получении ответа от сервера.

В проекте мы пользуемся JavaScript библиотекой jQuery (многие называют её JS-фреймворком, кому как угодно), поэтому и код AJAX запроса будет с использованием jQuery конструкций. Сам же код будет следующим:


$(document).ready(function(){
    $('#contactform').on('submit', function(e){
        e.preventDefault();

        $.ajax({
            type: 'POST',
            url: '/sendmail',
            data: $('#contactform').serialize(),
            success: function(result){
                console.log(result);
            }
        });
    });
});

Пару слов о том, что же здесь написано. Функция отправки AJAX запроса будет работать только когда страница загрузится, о чём говорит первая строка кода.

Запрос мы будем отправлять при событии submit HTML формы, т.е. при её подтверждении, что видно на второй строчке кода.

На третьей строке происходит прерывание стандартного подтверждения отправки формы. Если его не прервать, то вместо нашего кода Laravel пытался бы отправить запрос на action формы, которого нет, и мы получали бы ошибку о несуществующем URL.

На пятой строке можно увидеть вызов специальной функции jQuery ajax, которая делает отправку и обработку AJAX запросов невероятно удобной и простой, лишая разработчиков необходимости вникать в премудрости отправки и обработки результатов XMLHttpRequest из чистого JavaScript-кода.

Шестая, седьмая и восьмая строки говорят о том, что запрос будет отправляться методом POST на URL /sendmail и в теле запроса в качестве параметров будут передаваться данные, введённые пользователем в поля HTML формы.

Дальнейший код описывает действие, которое должно произойти при получении ответа от сервера. Пока что я решил прописать только вывод текста ответа в консоль браузера. В дальнейшем же, когда отправка email будет реализована, мы заменим данную «заглушку» на что-то более осмысленное.

В завершение описания данного этапа скажу, что код выше необходимо вставить в самый конец нашего кастомного JS-файла /public/js/app.js.  И неплохо будет почистить кэш браузера, иначе мы рискуем не увидеть результат выполнения.

Laravel AJAX — прописываем роуты

Итак, как вы могли заметить, в JS функции отправки AJAX запроса мы указали тип запроса и URL, на который он будет передан. Теперь самое время создать соответствующее правило Laravel routing для того, чтобы указать фреймворку как нужно обрабатывать данный запрос.

Для этого заходим в файл /routes/web.php и прописываем в его конце следующее правило:


Route::post('/sendmail', 'Ajax\ContactController@send');

Данный код говорит фреймворку, что при поступлении POST запроса на URL /sendmail следует вызвать метод контроллера Ajax\ContactController send().

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

Несмотря на то, что у нас уже есть контроллер ContactController, я решил для обработки AJAX запросов создать отдельный каталог с одноименным названием и заполнить его специальными одноимёнными контроллерами для обработки AJAX запросов на страницах сайта, т.к. считаю это архитектурное решение более правильным.

Если вы не разделяете мою точку зрения, можете создать метод send() в файле существующего контроллера /app/Http/Controllers/ContactController.php.

Я же воспользовался для создания нового контроллера уже знакомым алгоритмом.

Запускаем консоль и перемещаемся в каталог сайта:


cd c:/openserver/domains/laravel.portfolio

Выполняем artisan команду для создания нового контроллера:


php artisan make:controller Ajax/ContactController

После успешного выполнения должна была создаться папка /app/Http/Controllers/Ajax с файлом нового контроллера внутри ContactController.php.

Создаём в нём новый метод send(), который в качестве входного параметра будет принимать экземпляр класса Request, у которого в качестве свойств будут доступны тело, заголовок и параметры запроса, обрабатываемого данным методом.

Внутри данного метода в дальнейшем как раз и будет располагаться код отправки email сообщения. Пока же ограничимся «заглушкой», которая будет выводить на экран параметры запроса, которыми в нашем случае являются данные полей HTML формы.

В результате, контроллер Ajax/ContactController будет содержать следующий код:


<?php

namespace App\Http\Controllers\Ajax;

use Illuminate\Http\Request;
use App\Http\Controllers\Controller;

class ContactController extends Controller
{
    public function send(Request $request)
    {
        dd($request->all());
    }
}

Проверка выполнения Laravel AJAX запроса

Ну, вот и всё. Все необходимые приготовления выполнены. Осталось только проверить правильность работы нашей реализации Laravel AJAX.

Для этого переходим на страницу с AJAX формой обратной связи, а также открываем в браузере панель с инструментами разработчика и переходим на вкладку Console, где мы увидим ответ от сервера на AJAX запрос.

Если вы всё сделали правильно, то в консоли должны будут отобразиться введённые в поля формы данные в виде параметров запроса.

Итак, вводим произвольные данные в нашу HTML форму и нажимаем на кнопку «Отправить». В консоли вы должны увидеть следующее:

laravel-ajax-otvet-v-konsoli

Если вы увидели данный набор символов — поздравляю, вы всё сделали верно :-) Не обращайте внимание на то, что AJAX ответ от сервера пришёл в таком неприглядном виде, главное — это положительный результат!

Увиденное — это результат выполнения отладочной функции Laravel dd(), которая на самом деле на выходе содержит много HTML кода, скрытого от глаза пользователя. При выводе же HTML кода в консоли браузера он не обрабатывается, и мы на экране видим «голый» HTML и JavaScript.

Если же хотите увидеть Laravel AJAX ответ сервера во всей красе, стоит лишь переместиться на вкладку инструментов разработчика «Network» и нажать на /sendmail, чтобы увидеть следующее:

laravel-ajax-otvet-html

Как видите, сервер вернул список значений всех полей HTML формы, а также вывел его в консоли, что и было прописано нами в коде.

А это значит, что Laravel успешно выполнил и обработал AJAX запрос, который был нами отправлен через форму обратной связи.

Теперь дело осталось за малым — в контроллере вместо «заглушки» прописать действия, которые будут производиться на сервере при обработке данного запроса, а в JavaScript скрипте прописать сценарии для фронтэнда сайта, выполняемые при успешном получении результатов обработки запроса.

Этим мы и займёмся в следующей статье, где рассмотрим способы отправки email в Laravel более детально. Поэтому подписывайтесь на обновления по ссылке в начале статьи, чтобы получить уведомление на почту о выходе нового материала.

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

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

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

Пока :-)

  1. 5
  2. 4
  3. 3
  4. 2
  5. 1
7 голосов, в среднем: 5 из 5

14 комментариев к статье "Laravel AJAX: создаём форму обратной связи"

  1. Анатолий

    Здравствуйте!
    Хочу освоить данный фреймворк, подскажите стОящий курс для освоения.

    1. Pashaster Автор

      Добрый день! Так в чём проблема. Как раз такой курс и размещён на данном блоге. Кроме того, после его прохождения у Вас уже будет готовый корпоративный сайт на Laravel и первая работа в портфолио, если Вы только начинающий разработчик.

      Курс начинается с вводной статьи об особенностях Laravel PHP framework. И пишется по текущий момент, так что подписывайтесь на обновления проекта, чтобы получать уведомления на email о новых публикациях. Обещаю, никакого спама. Только анонсы новых статей.

      Ну, а если хотите форсировать события, то изучайте официальную документацию Laravel — https://laravel.com/docs/5.4. Она отлично структурирована и невероятно подробна и информативна.

      Всё просто :-)

      1. Аноним

        Спасибо за статьи. Можете пожалуйста после написания всех уроков по созданию этого сайта написать как создать магазин на Laravel.

        1. Pashaster Автор

          Спасибо Вам за отзыв :-) Про магазин — вы прямо мои мысли читаете))) Как раз собирался после корпоративного сайта. Но сразу скажу, от корпоративного сайта создание Laravel магазина будет отличаться только прикручиванием корзины и АПИ-шек разных платёжных сервисов и служб доставки.

          Админка у нас уже будет сделана в данном курсе — как раз после создания контактной формы для добавления статей на блог.

  2. Андрей

    Одно большое НЕТ, при работе с формами и роутами — никогда не используйте прямых ссылок. В ларавеле есть отличный хелпер route() — используйте его, и присваивайте своим роутам имена. Таким образом в роутере делается —

    $router->post(‘sendmail’, ‘Ajax\ContactController@send’)->name(‘ajax.contact.send’);

    В форме соответственно прописываем экшн, а в скрипте соответственно

    const $form = $("#contactsForm");
    
    $form.on("submit", (e) => {
         e.preventDefault();  //Тоже забыли, предотвращаем дефолтный сабмит формы.
         $.post($form.attr('action'), $form.serialize()).done( (response) => console.log(response); ); 
    });
    
    1. Pashaster Автор

      Спасибо за комментарий. Action контроллера в комментарии потерялся. Отправьте ещё раз, если не сложно, заключив его в шорткод code (в квадратных скобках).

      По поводу именованный роутов — честно говоря, не понял, в чём заключается категоричность их использования. Объясните, пожалуйста.

      По поводу JS-отправки/обработки запроса. Согласен, Ваш вариант лаконичней и более «умный» :-) Думаю, многим будет полезен.
      P.S. В своём я предотвратить дефолтный сабмит не забыл, т.к. без этого отправка запроса просто не производилась бы. Laravel пытался бы отправить запрос на action формы, которого нет, и вываливалась бы ошибка — проходили уже :-) Чтобы этого не происходило return false; в конце присутствует. Но, согласен, что это больше костыль, т.к. сам ивент не превентится. Наверное, исправлю лучше на Ваш вариант. Пусть люди учатся хорошим практикам. Спасибо.

  3. Андрей
    <form id="contactsForm" method="POST"  action="{{ route('ajax.contacts.send') }}"> 
    

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

    Кстати по той же причине весь раздел про хлебные крошки и миддлверы — это большой пример того как делать не надо.

    1. Pashaster Автор

      Ну, про такие случаи использования именованных роутов я знаю. Ладно бы, если их использование было бы как-то связано с вопросами безопасности или повышения производительности сайта…

      Но ради гипотетического удобства использования при изменении роутов (что бывает крайне редко) или прикручивания АПИ (что также случается очень часто) я лично не готов таким пользоваться, да и начинающим использование прямых урлов более понятно.

      Если в проекте есть реальная необходимость — тогда без вопросов.

      А по поводу Laravel middleware — что Вам там не понравилось? Если тот факт, что хлебные крошки через них реализуются, то я сам подчёркивал, что middleware — не самый лучший способ для этого. Я их сделал таким образом только в качестве примера использования middleware.

      1. Руслан

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

        1. Pashaster Автор

          Возможно, Вы и правы. Просто я пока ещё не сталкивался с такими ситуациями, поэтому и не вижу большой необходимости данного мероприятия. На одном из моих бывших долгоиграющих проектов если нужно было поменять url — сам шеф учил «Юзай Ctrl+f». Сложно от привычки избавиться) Да и действительно, забыл, когда последний раз с такой ситуацией сталкивался.

      2. Руслан

        Но это скорее все же больше рекомендация) Тут наверно и правда, на вкус и цвет фломастеры разные, кому-то нравится использовать алиасы кому-то нет. Критического НЕТ если честно я в коде автора не вижу)

        1. Pashaster Автор

          Спасибо на добром слове) Я тоже считаю, что это чисто вопрос удобства и привычки.

          Равно как и отправка почты в Laravel — около 10 драйверов для совершения одного действия разными способами. А на выходе один и тот же результат — письмо у конечного пользователя в почтовом ящике :-) Глупо было бы, как по мне, считать, что отправлять нужно ТОЛЬКО через SMTP, ссылаясь ещё и на то, что данный способ используется в движке по умолчанию (под него заточен дефолтный .env). Кому что нравится — то и используй :-)

          Это, наверное, одна из фишек Laravel, что для решения одинаковых задач фреймворк предоставляет несколько наборов порою разных инструментов. Надеюсь, холивар по поводу использования Eloquent и RAW-запросов начинать не будем :-)

  4. Edred

    Тут все ясно и просто. Но просьба, раз вы учите людей пользоваться Аяксом, то не забывайте про обработку ошибок в нем, то есть кроме success, надо прописывать и действия при error:

    [error: function(data) {
     ...
    }]
    

    Потому что если этому сразу людей не учить, то они и не будут иметь привычки обработку ошибок в javascript прописывать, а это ни к чему хорошему привести не может…

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

Ваш e-mail не будет опубликован. Обязательные поля помечены *