Полный цикл в digital

Стандартная форма обратной связи

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


Теперь идем в Сервисы -> Веб формы -> Настройка форм и жмем там кнопку Создать, чтобы создать форму:

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


Переходим во вкладку Вопросы и добавляем поля формы которые увидят пользователи, тут все интуитивно понятно:


Настройка полей для формы:

  • Имя
    • Вкладка Свойства
      • Активность: да
      • Символьный идентификатор: NAME
      • Обязателен: да
    • Вкладка Вопрос
      • Заголовок: Ваше имя
    • Вкладка Ответ
      • Текст [ANSWER_TEXT]: ставим пробел
      • Значение [ANSWER_VALUE]: оставляем пустым
      • Тип поля: text
      • Параметры: пусто
  • Телефон
    • Вкладка Свойства
      • Активность: да
      • Символьный идентификатор: TELEFON
      • Обязателен: да
    • Вкладка Вопрос
      • Заголовок: Номер телефона
    • Вкладка Ответ
      • Текст [ANSWER_TEXT]: ставим пробел
      • Значение [ANSWER_VALUE]: оставляем пустым
      • Тип поля: text
      • Параметры: phone

Создаем статус по умолчанию для результатов, если он не создался. Во вкладке Доступ для всех операций поставьте Создатель результата:


  1. Параметры формы вкладка с основными настройками формы
  2. Результат результат заполнения форм пользователями
  3. Вопросы поля которые увидят пользователи в форме
  4. Поля дополнительные поля которые будут отображаться в админке сайта и при выгрузки результатов в exel
  5. Статусы нужны для реализации сложной схемы работы с веб-формами, когда нужно не просто получить ответы на вопросы от посетителей сайта, но и обработать их: занести в CRM, составить списки по выборочным данным и так далее

Вывод формы

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

Открываем редактирование страницы в визуальном редакторе, в компонентах ищем form.result.new, перетягиваем в окно правки:


Через несколько секунд появится окно настройки компонента, нам надо заполнить поля:

  • ID веб-формы выбрать нашу форму
  • Использовать расширенный вывод сообщений об ошибках да
  • Включить поддержку ЧПУ нет
  • Страница со списком результатов пусто
  • Страница редактирования результата пусто
  • Страница с сообщением об успешной отправке пусто

После сохранения компонента, на странице появится форма. Она не очень красивая, поэтому установим свой шаблон. Включим режим правки, наведем курсор на форму, там скопируем шаблон, как показано на картинке:


Укажем новое название шаблона, шаблон сайта выберим текущий:


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

Редактирование шаблона

В template.php доступны следующие переменные.

Служебные

  • $arResult["isFormErrors"] равен Y, если есть ошибки с формой
  • $arResult["isFormNote"] равен Y, если форма успешно отправлена. С условием по этой переменной можно выводить сообщение об успешной отправке уже без формы
  • $arResult["FORM_HEADER"] открытие формы и системные теги
  • $arResult["FORM_FOOTER"] закрытие формы и системные теги
  • $arResult["QUESTIONS"] массив вопросов формы
  • $arResult["isUseCaptcha"] равно true, если включен показ капчи
  • $arResult["CAPTCHA_IMAGE"] тег img с картинкой капчи
  • $arResult["CAPTCHA_FIELD"] поле для ввода капчи
  • $arResult["F_RIGHT"] код права на доступ к текущей форме (вкладка Доступ формы)
  • $arResult["WEB_FORM_NAME"] символьный код формы
  • $arResult["arrVALUES"] значения полей введенные пользователем
  • $arResult["CAPTCHACode"] уникальный код капчи. Требуется для вывода картинки
  • $arResult["arForm"]["STAT_EVENT1"] event1 с вкладки Статистика формы
  • $arResult["arForm"]["STAT_EVENT2"] event2 с вкладки Статистика формы
  • $arResult["arForm"]["STAT_EVENT3"] event3 с вкладки Статистика формы

Информация пользователю

  • $arResult["FORM_ERRORS_TEXT"] текст ошибок
  • $arResult["FORM_NOTE"] сообщение об успешной отправке
  • $arResult["FORM_TITLE"] заголовок формы
  • $arResult["FORM_DESCRIPTION"] описание формы
  • $arResult["arForm"]["BUTTON"] текст кнопки Отправить
  • $arResult["REQUIRED_SIGN"] красная звездочка для подсвечивания обязательных полей

Структура вопроса

Для вывода полей надо брать вопросы из $arResult["QUESTIONS"] по символьному коду:

  • CAPTION имя поля
  • IS_HTML_CAPTION имя поля в формате HTML, Y/N.
  • REQUIRED обязательно к заполнению, Y/N.
  • IS_INPUT_CAPTION_IMAGE есть ли изображение вопроса
  • HTML_CODE HTML код поля
  • STRUCTURE детальное описание поля
  • IMAGE описание изображения вопроса, если IS_INPUT_CAPTION_IMAGE равен Y.

Например, чтобы вывести инпут поля с символьным кодом NAME надо сделать в шаблоне так:

<?=$arResult["QUESTIONS"]['NAME']['HTML_CODE']?>

На основе переменных выше соберем шаблон:

template.php<? if(!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED!==true)die(); ?>
<? if ($arResult["isFormNote"] === "Y"): ?>
  Спасибо, ваша заявка принята!
<? else: ?>
  <?= $arResult["FORM_HEADER"] ?>
  <input type="hidden" name="web_form_submit" value="Y">
  <? if ($arResult["isFormErrors"] === "Y"): ?>
      <div class="errors">
          <?=$arResult["FORM_ERRORS_TEXT"]?>
      </div>
  <? endif; ?>
  <?= $arResult["QUESTIONS"]['NAME']['CAPTION']?>
  <?= ($arResult["QUESTIONS"]['NAME']['REQUIRED'] === 'Y' ? ' *' : '')?>:
  <?= $arResult["QUESTIONS"]['NAME']['HTML_CODE']?>
  <br>
  <?=$arResult["QUESTIONS"]['TELEFON']['CAPTION']?>
  <?=($arResult["QUESTIONS"]['TELEFON']['REQUIRED'] === 'Y' ? ' *' : '')?>:
  <?=$arResult["QUESTIONS"]['TELEFON']['HTML_CODE']?>
  <br>
  <input type="submit" value="<?=$arResult["arForm"]["BUTTON"]?>">
  <?=$arResult["FORM_FOOTER"]?>
<? endif; ?>

Логика его такая:

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

Если форма успешно отправлена, выводить сообщение, иначе выводим форму.

Код выводит открытие формы со служебными скрытыми полями:

<?=$arResult["FORM_HEADER"]?>

Поле web_form_submit в значении Y триггер для Битрикс, говорящее о том, что форма отправлена и надо проверить, что там ввел пользователь.

Если есть ошибки - выводим их в диве с классом errors.

Перед закрытием формы, кнопка отправить с тексом, который указан в настройках формы.

Ajax отправка

Модернизируем наш шаблон для Ajax:

template.php<? if(!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED!==true)die(); ?>
  <?= $arResult["FORM_HEADER"] ?>
  <div class="error-msg"></div>
  <div class="success-msg"></div>
  <input type="hidden" name="web_form_submit" value="Y">
  <?= $arResult["QUESTIONS"]['NAME']['CAPTION']?>
  <?= ($arResult["QUESTIONS"]['NAME']['REQUIRED'] === 'Y' ? ' *' : '')?>:
  <?= $arResult["QUESTIONS"]['NAME']['HTML_CODE']?>
  <br>
  <?=$arResult["QUESTIONS"]['TELEFON']['CAPTION']?>
  <?=($arResult["QUESTIONS"]['TELEFON']['REQUIRED'] === 'Y' ? ' *' : '')?>:
  <?=$arResult["QUESTIONS"]['TELEFON']['HTML_CODE']?>
  <br>
  <input type="submit" value="<?=$arResult["arForm"]["BUTTON"]?>">
  <?= $arResult["FORM_FOOTER"]?>
<script>
    ajaxForm(document.getElementsByName('<?=$arResult['arForm']['SID']?>')[0], '<?=$templateFolder?>/ajax1.php')
</script>

Создайте в папке шаблона компонента файл script.js. Этот скрипт обрабатывает отправку формы, которая передается первым параметром, отправляя все данные из нее на ссылку, переданную вторым параметром:

script.jsfunction ajaxForm(obForm, link) {
// устанавливаем функцию обработчик
BX.bind(obForm, 'submit', BX.proxy(function(e) {
    // отменяем стандартное действие формы
    BX.PreventDefault(e);
    // очищаем вывод информации
    obForm.getElementsByClassName('error-msg')[0].innerHTML = '';
    obForm.getElementsByClassName('success-msg')[0].innerHTML = '';
    // объект для работы с Ajax
    let xhr = new XMLHttpRequest();
    // отправляем запрос к серверу 
    xhr.open('POST', link);
    // функция onload сработает, когда мы получим ответ
    xhr.onload = function() {
        // если ошибка, выводим алерт с ошибкой
        if (xhr.status != 200) {
            alert(`Ошибка ${xhr.status}: ${xhr.statusText}`);
        } else {
            // получаем ответа сервера в виде текста
            let json = JSON.parse(xhr.responseText)
            // если поля не заполнены
            if (! json.success) {
                let errorStr = '';
                for (let fieldKey in json.errors) {
                    errorStr += json.errors[fieldKey] + "<br>";
                }
                // ошибки вывести в элемент с классом error-msg
                obForm.getElementsByClassName('error-msg')[0].innerHTML = errorStr;
            } 
                // если сообщение отправленно
                if ( json.success) {
                    let successStr = json.errors;
                // положительный результат вывести в элемент с классом success-msg
                obForm.getElementsByClassName('success-msg')[0].innerHTML = successStr;
            }
        }
    };
    // передаем все данные из формы
    xhr.send(new FormData(obForm));
}, obForm, link));
}

Создаем в папке шаблона компонента, файл обработчик Ajax запросов:

ajax.php<?
require $_SERVER['DOCUMENT_ROOT'] . '/bitrix/modules/main/include/prolog_before.php';
// подключаем модуль веб-форм
CModule::IncludeModule("form");
// проверка валидности отправки формы
if (check_bitrix_sessid()) {
    // метод проверяет введенные значения на обязательность
    $formErrors = CForm::Check($_POST['WEB_FORM_ID'], $_REQUEST, false, "Y", 'Y');
  
    // если не все обязательные поля заполнены
    if (count($formErrors)) {
        echo json_encode(['success' => false, 'errors' => $formErrors]);
    } 
    // в случае успеха, возвращаем ID нового результата, в противном случае false
    elseif ($RESULT_ID = CFormResult::Add($_POST['WEB_FORM_ID'], $_REQUEST)) {
        // отправляем все события как в компоненте веб форм
        CFormCRM::onResultAdded($_POST['WEB_FORM_ID'], $RESULT_ID);
        CFormResult::SetEvent($RESULT_ID);
        CFormResult::Mail($RESULT_ID);
        // говорим что успешно заявку получили
        echo json_encode(['success' => true, 'errors' => "Сообщение успешно отправленно"]);
    } else {
        // если возникли ошибки
        echo json_encode(['success' => false, 'errors' => $GLOBALS["strError"]]);
    }
} else {
    // предотвращаем CSRF атаку
    echo json_encode(['success' => false, 'errors' => ['sessid' => 'Не верная сессия. Попробуйте обновить страницу']]);
}
// файл ниже подключать обязательно, там закрытие соединения с базой данных
require $_SERVER['DOCUMENT_ROOT'] . '/bitrix/modules/main/include/epilog_after.php';

Ajax отправка битрикс

Чтобы форма отправлялась без перезагрузки страницы, надо добавить в параметры вызова компонента следующее. С помощью этих параметров добавить Ajax можно в любой компонент. Эти параметры - не особенность вебформ, реализована работа с Ajax в базовом классе компонентов. Теперь Битрикс сам добавит нужные скрипты:

"AJAX_MODE" => "Y",
"AJAX_OPTION_SHADOW" => "N",
"AJAX_OPTION_JUMP" => "N",
"AJAX_OPTION_STYLE" => "Y",
"AJAX_OPTION_HISTORY" => "N",
template.php<? if(!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED!==true)die(); ?>
<? if ($arResult["isFormNote"] === "Y"): ?>
  Спасибо, ваша заявка принята!
<? else: ?>
  <?= $arResult["FORM_HEADER"] ?>
  <input type="hidden" name="web_form_submit" value="Y">
  <? if ($arResult["isFormErrors"] === "Y"): ?>
      <div class="errors">
          <?=$arResult["FORM_ERRORS_TEXT"]?>
      </div>
  <? endif; ?>
  <?= $arResult["QUESTIONS"]['NAME']['CAPTION']?>
  <?= ($arResult["QUESTIONS"]['NAME']['REQUIRED'] === 'Y' ? ' *' : '')?>:
  <?= $arResult["QUESTIONS"]['NAME']['HTML_CODE']?>
  <br>
  <?=$arResult["QUESTIONS"]['TELEFON']['CAPTION']?>
  <?=($arResult["QUESTIONS"]['TELEFON']['REQUIRED'] === 'Y' ? ' *' : '')?>:
  <?=$arResult["QUESTIONS"]['TELEFON']['HTML_CODE']?>
  <br>
  <input type="submit" value="<?=$arResult["arForm"]["BUTTON"]?>">
  <?=$arResult["FORM_FOOTER"]?>
<? endif; ?>

Алгоритм работы Ajax формы:

  1. Весь шаблон формы оборачивается в тег с уникальным ID
  2. После этого тега добавляется JS, который устанавливает событие на отправку формы
  3. Пользователь отправляет форму
  4. На сервер запрос через весь шаблон доходит до вызова компонента
  5. Битрикс обрабатывает введенные данные
  6. Получает вывод шаблона формы и заменяет на странице в теге, добавленном в пункте 1, всё содержимое
  7. Результат: страница не перезагружалась, пользователь видит вместо формы сообщение об успешной отправке или ту же форму с ошибками

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

Настройка капчи

Чтобы в форме появилась капча, добавьте в шаблон формы следующий код:

template.php<? if ($arResult["isUseCaptcha"] === true): ?>
  <?=$arResult["CAPTCHA_IMAGE"]?><br>
  Введите код с картинки:
  <?=$arResult["CAPTCHA_FIELD"]?>
<? endif; ?>

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

Бывает что код с картинки не читается и пользователь не может отправить форму, поможем пользователю. Вместо кода:

template.php<?=$arResult["CAPTCHA_IMAGE"]?>

Допишем следующие, если пользователь не может прочитать код, он может обновить картинку:

template.php<img src="/bitrix/tools/captcha.php?captcha_sid=<?=$arResult["CAPTCHACode"]?>"
  onclick="this.src = '/bitrix/tools/captcha.php?captcha_sid=<?=$arResult["CAPTCHACode"]?>&r='+Math.random()"
  style="cursor:pointer"
  width="180"
  height="40" >
Нажмите на картинку, чтобы обновить

Своя верстка полей

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

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

Требования к реализации:

  1. Для поля должен быть указан соответствующий тип type
  2. Требуется указать имя поля name
  3. Если форма отправлена и были ошибки, значения в поле не должны пропадать
  4. Если поле обязательно к заполнению, должен быть атрибут required
  5. Для полей ввода номера телефона добавим класс phone

В файле result_modifier.php шаблона формы добавим такой код:

result_modifier.php<?
if (!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED !== true) {
    die();
}
$arResult['funcGetInputHtml'] = function ($question, $arrVALUES, $isFormErrors) {
    // ID поля формы
    $id = $question['STRUCTURE'][0]['ID'];
    // тип поля формы
    $type = $question['STRUCTURE'][0]['FIELD_TYPE'];
    // имя поля формы
    $name = "form_{$type}_{$id}";
    // значение введенное пользователем
    $value = isset($arrVALUES[$name]) ? 'value="' .  htmlentities($arrVALUES[$name]) . '"' : '';
    // проверка на обязательное поле
    $required = $question['REQUIRED'] === 'Y' ? 'required' : '';
    // параметры из админки
    $parameters = $question['STRUCTURE'][0]['FIELD_PARAM'];
    // плейсхолдер поля формы
    $placeholder =  $question['CAPTION'] ? 'placeholder=' . '"' . $question['CAPTION'] . '"'   : "";
    // если поле обязательное, значение value введенное пользователем пустое, при отправке форма вернула ошибку
    if ($question['REQUIRED'] == 'Y' && empty($arrVALUES[$name]) && $isFormErrors == 'Y') {
        // добавляем класс для подсветки input
        $danger = true;
    }
    switch ($danger) {
            // если импут имеет ошибку
        case 'true':
            $input = "<input type=\"{$type}\" name=\"{$name}\" {$value} {$placeholder} {$parameters} style=\"border-color: red;\" > ";
            break;
            // если импут без ошибок
        default:
            $input = "<input type=\"{$type}\" name=\"{$name}\" {$value} {$placeholder} {$parameters} >";
            break;
    }
    return $input;
};

Мы создали функцию, которая попадет в файл template.php, установили имя, класс из параметров вопроса, атрибут обязательного поля, значение, введенное пользователем.

В template.php вместо вывода готового HTML кода поля:

template.php<?=$arResult["QUESTIONS"]['NAME']['HTML_CODE']?>

Выведем его через нашу функцию:

template.php<?=$arResult['funcGetInputHtml']($arResult["QUESTIONS"]['NAME'], $arResult['arrVALUES'], $arResult["isFormErrors"])?>

Вместо стандартного вывода, получим новый вывод.

Заполните форму уже сегодня!
Для начала сотрудничества необходимо заполнить заявку или заказать обратный звонок. В ответ получите коммерческое предложение, которое будет содержать индивидуальную стратегию с учетом требований и поставленных задач
Работаем по будням с 9:00 до 18:00. Заявки, отправленные в выходные, обрабатываем в первый рабочий день до 12:00.
Спасибо, ваш запрос принят и будет обработан!
Эйч Маркетинг