Интеграция форм с MODX: FetchIt + FormIt (на Fenom)

Интеграция форм MODX Курс

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

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

Что нам понадобится:

  • FormIt — «Мозг» (Backend). Он принимает данные, проверяет их (валидация) и отправляет письма.
  • FetchIt — «Курьер» (Frontend). Он отправляет данные из браузера на сервер без перезагрузки и принимает ответ (успех или ошибку).

Важно: убедитесь, что оба компонента установлены через «Установщик пакетов».

Подготовка: универсальный шаблон письма

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

Создайте файл чанка по пути: chunks/fetchIt/form.tpl.

Код чанка chunks/fetchIt/form.tpl:

<h3>Новая заявка с сайта</h3>

{if $name?}
    <p><strong>Имя:</strong> {$name}</p>
{/if}

{if $phone?}
    <p><strong>Телефон:</strong> {$phone}</p>
{/if}

{if $email?}
    <p><strong>Email:</strong> <a href="mailto:{$email}">{$email}</a></p>
{/if}

{if $url?}
    <p><strong>URL сайта (Аудит):</strong> <a href="{$url}" target="_blank">{$url}</a></p>
{/if}

{if $message?}
    <hr>
    <p><strong>Сообщение:</strong><br>
    {$message|nl2br}</p>
{/if}

Здесь мы проверяем наличие полей: если поле заполнено пользователем, оно добавится в письмо.

Часть 1: Форма «SEO Audit» (Простая)

Эта форма встречается у нас в блоке pb.seo_audit (Главная, Услуги) и в подвале сайта.

1. Создаем файл формы

Создайте файл: chunks/fetchIt/form_audit.tpl. Мы берем HTML из верстки и добавляем атрибуты для валидации и вывода ошибок.

Код чанка chunks/fetchIt/form_audit.tpl:

<form action="{$_modx->resource.id | url}" method="post" class="custom-form-style-1 form-errors-light" id="seoAuditForm">
    <div class="row mb-4">
        <div class="form-group col-md-6 pe-md-2">
            <input type="text" class="form-control {if $errors['url']}is-invalid{/if}" name="url" value="{$url}" placeholder="Enter URL" required />
            {if $errors['url']}<div class="invalid-feedback">{$errors['url']}</div>{/if}
        </div>
        <div class="form-group col-md-6 ps-md-2">
            <input type="email" class="form-control {if $errors['email']}is-invalid{/if}" name="email" value="{$email}" placeholder="Enter E-mail Address" required />
            {if $errors['email']}<div class="invalid-feedback">{$errors['email']}</div>{/if}
        </div>
    </div>
    
    <div class="row justify-content-center">
        <div class="form-group col-auto mb-0">
            {* Кнопка отправки *}
            <button type="submit" class="btn btn-quaternary btn-rounded font-weight-bold px-5 py-3 text-3">CHECK NOW</button>
        </div>
    </div>

    {* Сообщение об успехе (показывается через JS) *}
    <div class="alert alert-success d-none mt-4" id="seoSuccessMessage">
        Ваша заявка на аудит принята!
    </div>
</form>

2. Подключаем в PageBlock pb.seo_audit

Открываем чанк блока pb.seo_audit и заменяем старую верстку формы на вызов сниппета FetchIt.

Код вызова:

{'!FetchIt' | snippet : [
     'snippet' => 'FormIt',
     'hooks' => 'email',
     'validate' => 'url:required,email:required:email',
     'form' => '@FILE chunks/fetchIt/form_audit.tpl',
     'emailTpl' => '@FILE chunks/fetchIt/form.tpl',
     'emailTo' => $_modx->config.emailsender,
     'emailFrom' => 'noreply@site.com',
     'emailSubject' => 'Заявка на SEO Аудит',
     'successMessage' => 'Заявка успешно отправлена!'
]}

Важно:

  1. Мы используем @FILE, чтобы загружать чанк напрямую из файла.
  2. emailTo берет почту из системных настроек.
  3. emailFrom (от кого) лучше указывать на домене сайта (например, noreply@yoursite.com), чтобы письма не попадали в спам.
  4. Не забудьте также заменить форму на этот вызов в чанке подвала pb.footer_form!

Часть 2: Контактная форма (Сложная)

Переходим к странице Контактов. Там у нас был блок pb.contacts, содержащий большую форму с сообщением.

1. Создаем файл формы

Создайте файл: chunks/fetchIt/form_contacts.tpl.

Код чанка chunks/fetchIt/form_contacts.tpl:

<form class="contact-form custom-form-style-1" action="{$_modx->resource.id | url}" method="post" id="contactForm">
    
    {* Блок успеха (скрыт по умолчанию, покажем через JS) *}
    <div class="contact-form-success alert alert-success d-none mt-4">
        <strong>Success!</strong> Ваше сообщение успешно отправлено.
    </div>

    {* Блок ошибки (FormIt) *}
    <div class="contact-form-error alert alert-danger mt-4 {if !$errors}d-none{/if}">
        <strong>Error!</strong> Проверьте правильность заполнения полей.
    </div>

    <div class="row">
        <div class="form-group col mb-3">
            <input type="text" name="name" value="{$name}" class="form-control custom-bg-color-light-1 border-0 {if $errors['name']}is-invalid{/if}" placeholder="Your Name" required>
            {if $errors['name']}<span class="text-danger small">{$errors['name']}</span>{/if}
        </div>
    </div>
    
    <div class="row">
        <div class="form-group col mb-3">
            <input type="email" name="email" value="{$email}" class="form-control custom-bg-color-light-1 border-0 {if $errors['email']}is-invalid{/if}" placeholder="E-mail Address" required>
             {if $errors['email']}<span class="text-danger small">{$errors['email']}</span>{/if}
        </div>
    </div>
    
    <div class="row">
        <div class="form-group col mb-3">
            <textarea rows="6" name="message" class="form-control custom-bg-color-light-1 border-0 {if $errors['message']}is-invalid{/if}" placeholder="Your Message" required>{$message}</textarea>
             {if $errors['message']}<span class="text-danger small">{$errors['message']}</span>{/if}
        </div>
    </div>
    
    <div class="row">
        <div class="form-group col mb-3">
            <button type="submit" class="btn btn-gradient btn-rounded font-weight-bold px-5 py-3 text-3">SEND NOW</button>
        </div>
    </div>
</form>

2. Обновляем PageBlock pb.contacts

В чанке блока pb.contacts находим правую колонку с формой и вставляем туда вызов FetchIt:

{* ... Левая колонка с контактами ... *}

{* ПРАВАЯ КОЛОНКА: ФОРМА *}
<div class="col-md-10 col-lg-6 appear-animation" data-appear-animation="fadeInRightShorter" data-appear-animation-delay="2000">
    <div class="card border-0 custom-border-radius-1 box-shadow-1 p-2">
        <div class="card-body p-4 z-index-1">
            <h4 class="text-color-dark font-weight-semibold text-5 line-height-3 ls-0 mb-1 mt-2 pe-5 me-5">{$form_title}</h4>
            <p class="pb-2 mb-4">{$form_text}</p>

            {* ВЫЗОВ FETCHIT *}
            {'!FetchIt' | snippet : [
                'snippet' => 'FormIt',
                'form' => '@FILE chunks/fetchIt/form_contacts.tpl',
                'emailTpl' => '@FILE chunks/fetchIt/form.tpl',
                'hooks' => 'email',
                'emailTo' => $_modx->config.emailsender,
                'emailFrom' => 'noreply@site.com',
                'emailSubject' => 'Контактная форма с сайта',
                'validate' => 'name:required,email:required:email,message:required'
            ]}
        </div>
    </div>
</div>

Часть 3: Обработка ответов (JS)

FetchIt по умолчанию использует библиотеку Notyf (всплывающие уведомления). Это удобно, но в шаблоне Porto есть свои красивые alert блоки (зеленые плашки), которые мы сохранили в HTML (.contact-form-success).

Давайте научим FetchIt показывать именно их. Создайте файл assets/js/fetchit_custom.js (и подключите его в base.tpl после скриптов темы) или добавьте этот код в ваш custom.js:

document.addEventListener('DOMContentLoaded', () => {
    // 1. Слушаем событие успешной отправки формы
    document.addEventListener('fetchit:success', (e) => {
        const { form, response } = e.detail;

        // Логика для Контактной формы
        if (form.id === 'contactForm') {
            // Скрываем форму (опционально) или просто очищаем
            form.reset();
            
            // Показываем блок .contact-form-success
            const successAlert = form.querySelector('.contact-form-success');
            if (successAlert) {
                successAlert.classList.remove('d-none');
                successAlert.classList.add('d-block');
            }
            
            // Скрываем блок ошибок, если он виден
            const errorAlert = form.querySelector('.contact-form-error');
            if (errorAlert) errorAlert.classList.add('d-none');
        }

        // Логика для SEO Аудита
        if (form.id === 'seoAuditForm') {
             const successMsg = form.querySelector('#seoSuccessMessage');
             if (successMsg) successMsg.classList.remove('d-none');
             form.reset();
        }
    });

    // 2. Слушаем событие ошибки (если хотим показать общий алерт ошибки)
    document.addEventListener('fetchit:error', (e) => {
        const { form } = e.detail;
        
        if (form.id === 'contactForm') {
            const errorAlert = form.querySelector('.contact-form-error');
            if (errorAlert) {
                errorAlert.classList.remove('d-none');
                errorAlert.classList.add('d-block');
            }
        }
    });
});

Заключение

Теперь ваши блоки в PageBlocks не просто «мертвая верстка», а полноценные функциональные элементы.

Что мы сделали:

  1. Вынесли HTML форм в отдельные файлы чанков.
  2. Создали универсальный шаблон письма.
  3. Заменили верстку в блоках PageBlocks на вызов {'!FetchIt' | snippet : ...} с правильными путями через @FILE.
  4. Дописали JS-скрипт, чтобы интегрировать ответы сервера в дизайн шаблона Porto.

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

Оцените статью
MODX 3
Добавить комментарий

Этот сайт использует Akismet для борьбы со спамом. Узнайте, как обрабатываются ваши данные комментариев.