MODX. Создание и обработка форм с использованием FormIt

FormIt — компонент который обрабатывает поля формы после нажатия кнопки «Отправить». Не стоит расценивать его как компонент которым можно создавать форму, это лишь помощник в настройке уже подготовленной формы, избавляющий от написания своих PHP скриптов.

У FormIt есть Хуки (Hooks) — скрипты, которые выполняются во время обработки. Хуки могут быть линейными, т.е. выполняются по очереди, а могут быть последовательными, т.е. если выполнено условие первого хука, то начнется выполнение следующего хука.

Существуют также Прехуки (Pre-Hooks), скрипты, которые выполняются до загрузки формы. К примеру скрипт который устанавливает значение по умолчанию для полей форм в старых браузерах, которые не поддерживают соответствующие html плейсхолдеры.

Можно создавать собственные хуки и прехуки указывая их в параметрах preHook и hook FormIt. Стоит учитывать, что они будут выполняться в порядке указанном при вызове, поэтому, если один из скриптов выдал ошибку, сломаются и последующие.

Подключение простой формы с использованием FormIt

Код полностью

[<!--code-->[!FormIt?
   &hooks=`email`
   &emailFrom=`donotreply[@]yourdomain.com`
   &emailTpl=`mailtpl.feedback`
   &emailTo=`donotreply@yourdomain.com, admin@yourdomain.com`
   &emailSubject=`Письмо с сайта yourdomain.com`
   &successMessage=`Сообщение отправлено`
   &validate=`name:required,
      email:email:required,
      comment:required,
      antispam:blank`
]]

<form action="" method="post">
    <label for="name">Name:</label>
    <input type="text" name="name" id="name" value="[<!--code-->[!+fi.name]]">
    <label for="email">Email</label>
    <input type="text" name="email" id="email" value="[<!--code-->[!+fi.email]]">
    <label for="comment">Comment</label>
    <textarea name="comment" id="comment" cols="30" rows="10" value="[<!--code-->[!+fi.comment]]"></textarea>
    <input type="text" name="antispam" id="antispam" value="">
    <input type="submit" value="Submit">
</form>

[[!+fi.validation_error_message:!empty=`
<div class="alert">
  <h3>Пожалуйста, исправьте следующие ошибки:</h3>
    <ul>
        [[!+fi.error.name:!empty=`<li><a href="blog/web/back-end/modx/formit/#name">Поле «Имя» не заполнено</a></li>`]]
        [[!+fi.error.email:!empty=`<li><a href="blog/web/back-end/modx/formit/#email">Поле «Email» не заполнено</a></li>`]]
        [[!+fi.error.comment:!empty=`<li><a href="blog/web/back-end/modx/formit/#comment">Поле «Комментарий» не заполнено</a></li>`]]
    </ul>
</div>`]]
  1. В нужном месте шаблона, или на отдельной странице создадим html код формы
  2. <form action="" method="post">
        <label for="name">Имя:</label>
        <input type="text" name="name" id="name" >
        <label for="email">Email</label>
        <input type="text" name="email" id="email" >
        <label for="comment">Комментарий</label>
        <textarea name="comment" id="comment" cols="30" rows="10"></textarea>
        <input type="submit" value="Submit">
    </form>

    Добавим некешируемые плейсхолдеры FormIt ([[!+fi.код_поля]])

    <form action="" method="post">
        <label for="name">Name:</label>
        <input type="text" name="name" id="name" value="[[!+fi.name]]">
        <label for="email">Email</label>
        <input type="text" name="email" id="email" value="[[!+fi.email]]">
        <label for="comment">Comment</label>
        <textarea name="comment" id="comment" cols="30" rows="10" value="[[!+fi.comment]]"></textarea>
        <input type="submit" value="Submit">
    </form>

    (префикс fi при желании можно изменить свойством placeholderPrefix при вызове сниппета)

  3. Создаем чанк письма, которое будет приходить на почту после отправки результатов формы. К примеру mailtpl.feedback:

    /* В качестве плейсхолдеров указывается код поля,
     который был записан в форме после префикса fi*/
    <p>Имя: [[+name]]</p>
    <p>Email: [[+email]]</p>
    <p>Комментарий: [[+comment]]</p>     
  4. Вызываем сниппет FormIt 

    [[!FormIt?
       &hooks=`email`
       &emailFrom=`donotreply@yourdomain.com`
       &emailTpl=`mailtpl.feedback`
       &emailTo=`donotreply@yourdomain.com, admin@yourdomain.com`
       &emailSubject=`Письмо с сайта yourdomain.com`
       &successMessage=`Сообщение отправлено`
    ]]
  5. К коду выше добавляем параметры для проверки заполненности обязательных полей и другие параметры проверки:

    &validate=`name:required,
          email:email:required,
          comment:required`
    
    /* К полю email добавлен параметр :email,
    он проверяет правильность написания адреса электронной почты */
    
    
  6. Добавим оповещение пользователя, о неправильно заполненном поле. В специально отведенное для сообщения об ошибке место (к примеру в тег <label>) вставляем код: 

    [[!+fi.error.placeholder_code:notempty=`<span>Обязательное поле не заполнено</span>`]]

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

    [[!+fi.validation_error_message:!empty=`
    <div class="alert">
      <h3>Пожалуйста, исправьте следующие ошибки:</h3>
        <ul>
            [[!+fi.error.name:!empty=`<li><a href="[[*uri]]#name">Поле «Имя» не заполнено</a></li>`]]
            [[!+fi.error.email:!empty=`<li><a href="[[*uri]]#email">Поле «Email» не заполнено</a></li>`]]
            [[!+fi.error.comment:!empty=`<li><a href="[[*uri]]#comment">Поле «Комментарий» не заполнено</a></li>`]]
        </ul>
    </div>`]]

Анти-спам

  • Капча (captcha) — распространенный метод. Некоторые виды этой защиты боты научились распознавать и обходить. Самой эффективной на данный момент является reCAPTCHA от Google. Минусы — дополнительное поле, которое надо заполнить пользователю.
    Для FormIt есть специальные хуки с капчами math и reCaptcha (смотрите в резделе Хуки);
  • Скрытые поля ввода
    /* Скрытое поле. Не эффективный метод, т.к. боты научились не заполнять скрытые поля */
    <input type="hidden" name="antispam" value=""> 
    
    /* Обычное поле, скрытое за счет css */
    <input type="text" id="antispam" value="">
    
    /* Такие поля можно добавлять с использованием JS, 
    это усилит защиту, т.к. большинство ботов не использует js */
    
    /* При использовании скрытых полей в FormIT надо добавить проверку*/
    [[!FormIt? &validate=`antispam:blank`]]
  • Для FormIt есть специальный хук; проверяющий указанный email в спам-листе. Данный метод помогает далеко не всегда.
    [[!FormIt? &hooks=`spam` &spamCheckIp=`true`]]
  • Метод от Ильи Уткина, добавить замаскированное поле, которое будет отвечать на спам успешной отправкой, благодаря чему спам-бот и его хозяин сочтут что сообщение отправлено:

/* Создаём сниппет checkSpam */
<?php
if ($_POST['surname']) { // проверяем наше поле на пустоту
    echo $AjaxForm->success('Ваше сообщение отправлено');
    die();
} else {
    return true;
}

/* Добавляем в форму поле, по которому будем фильтровать */
<input type="text" name="surname" class="form-input" placeholder="Фамилия">

/* Маскируем поле */
input[name="surname"] {
    display: block;
    width: 2px;
    height: 3px;
    margin-bottom: -3px;
    opacity: 0.01;
}

/* Добавляем сниппет checkSpam в качестве хука перед email */
`hooks` => `checkSpam,email...`

AjaxForm

Сниппет ajaxForm представляет из себя надстройку над FormIt реализующую Ajax вызов. Вызов сниппета

[[!AjaxForm?
    &snippet=`FormIt`
    &form=`tpl.AjaxForm.example`
    &emailTpl=`mailtpl.feedback`
    &hooks=`email`
    &emailSubject=`Письмо с сайта yourdomain.com`
    &emailFrom=`donotreply[@]yourdomain.com`
    &emailTo=`donotreply@yourdomain.com, admin@yourdomain.com`
    &validate=`name:required,
      email:email:required,
      comment:required,
      antispam:blank`
    &successMessage=`Сообщение отправлено`
]]
   

Подробнее на странице об AjaxForm

Параметры FormIt

  По умолчанию Описание
hooks   Скрипты, которые запускаются после того как нажимается кнопка отправки. Сюда также можно записать имя сниппета, который должен воспроизводиться после отправки формы.
preHooks   Скрипты, которые запускаются после загрузки формы. Сюда также можно записать имя сниппета, который должен воспроизводиться после отправки формы.
submitVar  

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

// запись при вызове
&submitVar=`contactform`

// в форме добавляется скрытое поле с аналогичным именем
<input type="hidden" name="contactform" value="1" />

 

validate   Разделенный запятыми, список полей, для проверки. Формат записи name:validator (например: username:required, email:email:required). Валидаторов у поля может быть несколько.
validationErrorMessage A form validation error occurred. Please check the values you have entered. Текст сообщения об ошибке. Может содержать , если надо отобразить список всех ошибок.
validationErrorBulkTpl [[+error]] Шаблон сообщения об ошибке валидации
errTpl [[+error]] Оболочка html для сообщений об ошибках. Примечание: не чанк, только HTML.
customValidators   Разделенный запятыми, список имен пользовательских валидаторов (для параметра validate), которые будут использоваться в этой форме. Они должны быть явно указаны здесь, иначе не будут запущены.
clearFieldsOnSuccess 1 Если true, очистит поля после успешной отправки формы, которая не будет перенаправлена.
store 0 Если true, сохранит данные в кэше для извлечения, с использованием компонента FormItRetriever.
storeTime 300 (5 минут) Если для параметра store установлено значение true, этот параметр указывает количество секунд для хранения данных из представления формы. 
storeLocation cache При использовании хранилища это определяет, где форма сохраняется после отправки. Возможными параметрами являются «cache» и «session»
placeholderPrefix

fi.

Префикс для плейсхолдеров полей формы, которые должен обрабатывать FormIt. Разделитель в виде точки '.' обязателен. 
successMessage   Если не используется переадресация, отобразите это сообщение об успешном завершении после успешной отправки.
successMessagePlaceholder fi.successMessage Имя плейсхолдера, для размещения сообщения об успешной отправке.
redirectTo   ID страницы, с сообщением отображающимся после отправки формы. Является частью хука redirect, работает только с его подключением.
allowFiles true Разрешены ли файлы для публикации. Отправленные файлы хранятся во временном каталоге, для предотвращения потери файлов в многоэтапных формах.

 

Хуки (hooks)

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

Список предустановленных хуков

Email — отправляет результат формы на почту (активирует функцию mail()).

Доступные настройки: 

  • emailTpl — обязательный параметр. Шаблон письма который должен приходить на почту.
  • emailSubject — тема письма
  • emailUseFieldForSubjectесли установлен параметр 1, и поле emailSubject не установлено, то в качестве темы письма будет использоваться поле.
  • emailTo — разделенный запятыми список почтовых адресов, на который отправлять письма.
  • emailToName — необязательный параметр. Разделенный запятыми список пар Имя/Адрес, на который отправлять письма.
  • emailFrom — необязательный параметр. Адрес электронной почты который будет указан в качестве отправителя. Если не установлен, будет указан первый найденный адрес по следующему списку: поле email из формы, параметр emailsender из настроек системы. Стоит учесть, что в качестве отправителя следует указывать существующий адрес электронной почты, во избежании проблем с попаданием писем в спам, из-за нарушений правил SPF / DMARC.
  • emailFromName — необязательный параметр. Имя которое должно быть указано в поле From.
  • emailHtml — необязательный параметр. Разрешена ли отправка письма в html формате. По умолчанию — 1, разрешена.
  • emailConvertNewlines — необязательный параметр. Если установлено 1, все символы новой строки преобразуются в теги br.
  • emailReplyTo — электронная почта для отправки ответа. Если не задано, отправляется на адрес указанные в поле email, если таковой отсутствует, используется параметр emailFrom.
  • emailReplyToName — необязательный параметр. Имя для параметра emailReplyTo.
  • emailCC — разделенный запятыми список адресов для отправки копий сообщения (CC).
  • emailCCName — необязательный параметр. Разделенный запятыми список пар Имя/Адрес, для отправки копий сообщения (СС)
  • emailBCC — разделенный запятыми список адресов для отправки скрытых копий сообщения (BCC).
  • emailBCCName — необязательный параметр. Разделенный запятыми список пар Имя/Адрес, для отправки скрытых копий сообщения (BСС)
  • emailMultiWrapper — обертка для отправленных значений параметров типа чекбокс / мультивыбор. По умолчанию отправляются только значения.
  • emailMultiSeparator — разделитель для отправленных значений параметров типа чекбокс / мультивыбор. По умолчанию новая строка.

FormItAutoResponder — отправляет автоответ автору сообщения.

  • fiarTpl — обязательный. Шаблон для сообщения автоответчика;
  • fiarSubject — тема письма;
  • fiarToField — имя поля формы для использования в качестве электронной почты подателя. По умолчанию «электронная почта».
  • fiarFrom — необязательный. Если установлено, укажите адрес From: для электронной почты. По умолчанию используется настройка системы электронной почты.
  • fiarFromName — необязательный. Значение From: для электронной почты.
  • fiarSender — необязательный. Заголовок отправителя электронной почты. По умолчанию используется настройка системы электронной почты.
  • fiarHtml — необязательный. Должно ли электронное письмо должно быть в формате HTML. Значение по умолчанию равно true.
  • fiarReplyTo — обязательный. Адрес электронной почты для ответа.
  • fiarReplyToName — необязательный. Имя поля Reply-to.
  • fiarCC — список электронных писем для отправки через cc.
  • fiarCCName — необязательный. Список имен, разделенных запятыми, для сопряжения с значениями fiarCC.
  • fiarBCC — список писем с разделителями-запятыми для отправки через ОЦК.
  • fiarBCCName — необязательный. Список имен, разделенных запятыми, для сопряжения с значениями fiarBCC.
  • fiarMultiWrapper — обнуляет значения, представленные флажками / мультивыбираемыми с этим значением. По умолчанию используется только значение.
  • fiarMultiSeparator — отделяет флажки / мультивыборы с этим значением. По умолчанию используется новая строка. ( "\ П")
  • fiarFiles — необязательный. Список файлов, разделенных запятыми, для добавления в качестве вложения в электронное письмо. Вы не можете использовать URL-адрес здесь, а только локальный путь к файловой системе.
  • fiarRequired — необязательный. Если установлено значение false, крюк FormItAutoResponder не останавливается, когда поле, определенное в 'fiarToField', остается пустым. Значение по умолчанию равно true.

math — добавляет математическую капчу к форме.

Подобная капча выглядит как вопрос формата: 12 + 23? 

Для ее интеграции надо добавить math к параметру &hooks и валидацию к соответствующему полю

&hooks=`math,email`
&validate=`math:required`

В нужное место формы вставить вызов

<label>[[!+fi.op1]] [[!+fi.operator]] [[!+fi.op2]]?</label>
[[!+fi.error.math]]
<input type="text" name="math" value="[[!+fi.math]]" />
<input type="hidden" name="operator" value="[[!+fi.operator]]" />

 Дополнительные параметры хука math

    По умолчанию
mathMinRange Минимальное допустимое число 10
mathMaxRange Максимальное допустимое число 100
mathField Имя поля с ответом math
mathOp1Field Устаревший параметр. Имя поля для первого числа в уравнении. op1
mathOp2Field Устаревший параметр. Имя поля для второго числа в уравнении. op2
mathOperatorField Имя поля определяющего оператор уравнения operator

Кастомизация хука math

<label>[[!+fi.op1]] [[!+fi.operator:is=`-`:then=`minus`:else=`plus`]] [[!+fi.op2]]?</label>

Подобная запись выведет уравнение формата "23 plus 41?" или "50 minus 12?" вместо операторов -/+. Подобная запись немного усложнит задачу ботам.

recaptcha — добавляет рекапчу к форме.

Хук recaptcha предназначен для вставки в форму устаревшей версии рекапчи от гугл. На момент написания статьи лучше установить и использовать хук recaptchav2.

Для использования recaptcha надо добавить подключение в параметр hooks 

&hooks=`recaptcha,email`

и вставить следующие теги в нужное место формы 

[[+formit.recaptcha_html]]
[[!+fi.error.recaptcha]]

затем прописать ключи google в системные настройки:

  • formit.recaptcha_private_key — секретный ключ;
  • formit.recaptcha_public_key — ключ сайта;
  • formit.recaptcha_use_ssl — использовать ssl для запросов рекапчи. По умолчанию false.

Дополнительные настройки хука recapture

  • recaptchaJs — обьект JSON для вставки в параметры рекапчи, который будет генерировать соответствующий виджет. По умолчанию {}.
  • recaptchaTheme — шаблон рекапчи который должен использоваться. По умолчанию не указан. 

Ключи можно получить на странице google recapture

redirect — перенаправляет пользователя на специальный ресурс, при завершении отправки.

  • RedirectTo — обязательный параметр. Идентификатор ресурса на который надо перенаправить пользователя после успешной отправки формы.
  • redirectParams — объект параметров JSON для передачи URL-адреса переадресации.

Пример: 

[[!FormIt?
   &hooks=`redirect`
   &redirectTo=`212`
   &redirectParams=`{"user":"123","success":"1"}`
]]

<!-- 
С redirectParam формат полученного адреса будет таким
test.html?user=123&success=1 
-->

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

spam — проверяет все поля формы, указанные в свойстве spamEmailFields, на фильтр спама через StopForumSpam. Если пользователь помечен как спамер, форма покажет сообщение об ошибке.

  • spamEmailFields — необязательный. Список полей электронной почты с разделителями-запятыми для проверки. По умолчанию «электронная почта».
  • spamCheckIp — если true, будет также проверяться IP-адрес пользователя. По умолчанию false.
[[!FormIt? &hooks=`spam`]]

Не рекомендуется использовать IP для проверки спама, поскольку спамеры могут легко изменять IP-адреса, а проверка IP-адресов может выдать ложное срабатывание. 

FormItSaveForm — хук для сохранения результатов формы внутри панели менеджера.

Список расположен в Приложения (Extras) => FormIt. Доступные настройки:

  • formName: имя формы. По умолчанию «form-{resourceid}».
  • formEncrypt: если установлено ’1′ (true) — отправленная форма будет зашифрована перед сохранением в базе.
  • formFields: разделенный запятыми список полей, которые будут сохранены. По умолчанию сохраняются все поля, включая кнопку отправки.
  • fieldNames: изменение имени полей внутри панели менеджера. Если поле называется email2 вы можете изменить имя на «второй email». Пример: &fieldnames=`fieldname==newfieldname, anotherone==anothernewname`.

Дополнительные хуки 

Помимо предустановленных хуков можно писать свои или скачивать их с репозиториев. Для написания своего хука можно использовать сниппет, указав в параметре hooks его название.

Список полезных хуков которые можно скачать:

recaptchav2 — хук для интеграции с рекапчей от гугл второй и третьей версии.

Для интеграции надо добавить подключение в параметр hooks:

&hooks=`recaptchav2,email`

// для 3 версии
   &hooks=`recaptchav3,email`

и вставить следующие теги в нужное место формы:

    [[!recaptchav2_render]]
    [[!+fi.error.recaptchav2_error]]

// для 3 версии
    [[!recaptchav3_render]]
    [[!+fi.error.recaptchav3_error]]

затем прописать ключи google в системные настройки:

  • recaptchav2.secret_key — секретный ключ;
  • recaptchav2.site_key — ключ сайта.

для рекапси 3 версии используйте соответствующие настройки:

  • recaptchav3.secret_key — секретный ключ;
  • recaptchav3.site_key — ключ сайта.

Ключи можно получить на странице google recapture

Селекты, чекбоксы, переключатели

(selects, checkboxes, radio buttons)

Для множественных полей формы, таких как селекты, чекбоксы и переключатели, стандартный способ подключения не сработает. Для таких случаев в FormIt предусмотрены сниппеты FormItIsSelected и FormItIsChecked.

Оформление select поля

<select name="color">
   <option value="blue" [[!+fi.color:FormItIsSelected=`blue`]] >Синий</option>
   <option value="red" [[!+fi.color:FormItIsSelected=`red`]] >Красный</option>
   <option value="green" [[!+fi.color:FormItIsSelected=`green`]] >Зеленый</option>
   <!-- Способ заполнения с использованием phx фильтров -->
   <option value="yellow" [[!+fi.color:is=`yellow`:then=`selected`]]>Желтый</option>
</select>

Оформление checkbox и radio поля

<label>Color: [[!+fi.error.color]]</label>
<input type="checkbox" name="color[]" value="blue" [[!+fi.color:FormItIsChecked=`blue`]] > Синий
<input type="checkbox" name="color[]" value="red" [[!+fi.color:FormItIsChecked=`red`]] > Красный
<input type="checkbox" name="color[]" value="green" [[!+fi.color:FormItIsChecked=`green`]] > Зеленый

У html чекбоксов есть особенность — если значение отсутствует, параметр не передаётся, это так же влияет на проверку заполненности поля. Для того, чтобы отправить пустое значение, необходимо добавить скрытое input поле

[[!FormIt? &validate=`color:required`]]
...
<label>Color: [[!+fi.error.color]]</label>
<input type="hidden" name="color[]" value="" />
<input type="checkbox" name="color[]" value="blue" [[!+fi.color:FormItIsChecked=`blue`]] > Синий
<input type="checkbox" name="color[]" value="red" [[!+fi.color:FormItIsChecked=`red`]] > Красный
<input type="checkbox" name="color[]" value="green" [[!+fi.color:FormItIsChecked=`green`]] > Зеленый

 Таким образом можно проверить, выбран ли хотябы 1 флажок. 

Если надо установить поле с массивом значений (группу флажков, с одинаковым именем, или множественный селект) в преХук, надо сперва раскодировать данные из json формата

$hook->setValue('цвет',json_encode(array('синий','красный','зеленый')));

Настройка целей для счетчиков

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

&successMessage=`Ваше письмо успешно отправлено <script>yaCounterXXXXXXXX.reachGoal('form');</script>`

Возможные ошибки

— Если FormIt говорит об успешной отправке формы, но письмо на почту не приходит, стоит проверить

  1. правильную настройку mx записи на сервере;
  2. в вызове formIt указан ли адрес исходящей почты: &emailFrom=`mail@site.ru`

— Если FormIt или AjaxForm выдают 500 ошибку при отправке, причин может быть несколько, и все они связаны с проблемами на сервере. К примеру, если на сервере используется php ниже 5.4 (не рекомендуется для новых версий modx), то проблему можно решить установкой в файле .htaccess следующего параметра:

php_flag register_globals off

Если же на сервере стоит версия php 5.4 и выше, то искать проблему стоит в логах Modx (core/cache/logs/error.log) и сервера. Если записей в логах нет,  попробуйте переустановить компоненты FormIt и AjaxForm, и посмотрите, не вылезают ли ошибки во время установки. Если компонент ругается на ошибки, проставьте соответствующим папкам на сервере права доступа 700. И попробуйте переустановить снова (кнопка reinstall может не помочь, иногда эффективнее воспользоваться uninstall). Подробнее об ошибке 500.

— Если в списке отправленных форм, на странице FormIt в админ панели, вместо нормального списка отображается массив символов — вероятно вы раньше времени перешли на php 7.1, спуститесь на 7.0, либо ждите фикса компонента.

 

Документация FormIt

Документация AjaxForm