Skip to content

Все для WordPress, веб-розробки — і не тільки

🚀 Як підключитися до Contact Form 7 перед відправкою форми

🚀 Як підключитися до Contact Form 7 перед відправкою форми

Contact Form 7 встановлений на мільйонах сайтів WordPress. Він простий, надійний і безплатний. Але щойно стандартної поведінки перестає вистачати, треба валідувати поле, підставити динамічного отримувача або скасувати лист за умови, починається зона «як це взагалі робиться».

Документація CF7 описує хуки коротко. На форумах і Stack Overflow лежать сніпети під старі версії плагіна, і код п’ятирічної давнини мовчки ламається на сучасному CF7. Розбираємося, які хуки актуальні сьогодні та як до них підключатися без сюрпризів.

💡 Швидкий огляд:

  • головний хук wpcf7_before_send_mail, точка входу для будь-якої логіки перед надсиланням.
  • пропустити лист можна фільтром wpcf7_skip_mail або одним рядком skip_mail: on у налаштуваннях форми.
  • отримати введені дані через об’єкт $submission і метод get_posted_data().
  • змінити отримувача динамічно через set_properties() на об’єкті форми.
  • для валідації полів використовуйте wpcf7_validate з фільтром за типом поля.

Стандартний сценарій CF7 виглядає так: користувач заповнює поля, натискає «Надіслати», плагін шле лист на фіксовану адресу. Усе. Додати логіку всередину цього процесу без хуків неможливо.

Хуки wpcf7_before_send_mail і супутні фільтри дозволяють вклинитися в ланцюжок рівно в той момент, коли дані вже перевірені, але лист ще не пішов. Ви можете:

  • перевірити кастомну умову та скасувати надсилання;
  • зберегти дані форми в базу або зовнішній API;
  • змінити отримувача листа на льоту;
  • модифікувати вміст листа;
  • запустити інтеграцію з CRM або розсилкою.

Усі приклади нижче працюють із Contact Form 7 версії 5.2 і вище. Якщо ваш плагін старіший, спочатку оновіть: старі сигнатури хуків несумісні.

Wpcf7_before_send_mail, точка входу

З версії 5.2 сигнатура хука змінилася. Раніше передавався тільки об’єкт форми, тепер три аргументи:

1add_filter( 'wpcf7_before_send_mail', 'cf7_before_send_handler', 10, 3 );
2
3function cf7_before_send_handler( $contact_form, $abort, $submission ) {
4 // $contact_form — объект WPCF7_ContactForm
5 // $abort — флаг отмены (true/false)
6 // $submission — объект WPCF7_Submission с данными
7
8 // ваша логика здесь
9
10 return $contact_form;
11}

Файл для розміщення: functions.php активної теми або плагін Code Snippets. Пріоритет 10 підходить для більшості завдань; підіймайте до 1, якщо ваш callback має відпрацювати раніше за інші, або опускайте до 99, якщо після.

І зверніть увагу: хук, фільтр, не екшн. Повертати потрібно об’єкт $contact_form (або WPCF7_ContactForm), інакше ланцюжок перерветься некоректно.

Пропуск надсилання листа

Найчастіший кейс: не слати сповіщення, якщо поле заповнене певним чином. Для цього є два шляхи.

Через фільтр wpcf7_skip_mail

Коротко й чисто. Фільтр приймає булеве значення та об’єкт форми:

1add_filter( 'wpcf7_skip_mail', 'maybe_skip_mail', 10, 2 );
2
3function maybe_skip_mail( $skip_mail, $contact_form ) {
4 $submission = WPCF7_Submission::get_instance();
5
6 if ( $submission ) {
7 $data = $submission->get_posted_data();
8
9 // не шлём письмо, если поле your-reason равно 'test'
10 if ( 'test' === ( $data['your-reason'] ?? '' ) ) {
11 $skip_mail = true;
12 }
13 }
14
15 return $skip_mail;
16}

Метод WPCF7_Submission::get_instance() дає доступ до даних надсилання всередині цього фільтра. Без нього get_posted_data() недоступний, $submission не передається напряму в wpcf7_skip_mail.

Через Additional Settings

Якщо логіка простіша нікуди, завжди пропускати лист для конкретної форми, ідіть в адмінку. Вкладка форми «Додаткові налаштування» (Additional Settings), один рядок:

1skip_mail: on

Жодного коду. Плагін перериває надсилання листа одразу після валідації, а користувач бачить повідомлення про успішне надсилання. Для демоформ і тестових середовищ, ідеально.

Альтернатива з тим самим ефектом: demo_mode: on. Різниця в тому, що demo_mode повністю симулює успіх без торкання поштової підсистеми, а skip_mail пропускає тільки крок надсилання, зберігаючи всю решту поведінки.

Отримання введених даних

Об’єкт $submission (третій аргумент wpcf7_before_send_mail) дає повний доступ до того, що надіслав користувач:

1add_filter( 'wpcf7_before_send_mail', 'cf7_read_form_data', 10, 3 );
2
3function cf7_read_form_data( $contact_form, $abort, $submission ) {
4 // все поля разом
5 $posted = $submission->get_posted_data();
6
7 // конкретное поле (ключ — name атрибут тега в форме)
8 $user_name = $submission->get_posted_data( 'your-name' );
9 $user_email = $submission->get_posted_data( 'your-email' );
10
11 // ID поста, с которого отправлена форма
12 $post_id = $submission->get_meta( 'container_post_id' );
13
14 // ID формы
15 $form_id = $contact_form->id();
16
17 // сохраняем в лог или внешний сервис
18 if ( $user_email ) {
19 // например, добавить подписчика в рассылку
20 }
21
22 return $contact_form;
23}

Ключі полів ('your-name', 'your-email') — це значення атрибута name у шорткодах форми. Стандартні теги [text* your-name], [email* your-email] дають ключі без префіксів, просто копіюйте те, що після пробілу.

Важливо: get_posted_data() повертає сирі дані до обробки поштовими шаблонами. Спецсимволи, перенесення рядків, усе як ввів користувач. Санітизуйте перед збереженням у базу через sanitize_text_field() або аналоги.

Динамічна зміна отримувача

Припустімо, лист має йти різним менеджерам залежно від обраної у формі теми. Змінюємо властивість recipient на льоту:

1add_filter( 'wpcf7_before_send_mail', 'cf7_dynamic_recipient', 10, 3 );
2
3function cf7_dynamic_recipient( $contact_form, $abort, $submission ) {
4 $data = $submission->get_posted_data();
5 $department = $data['your-department'] ?? '';
6
7 $recipients = [
8 'sales' => '[email protected]',
9 'support' => '[email protected]',
10 'billing' => '[email protected]',
11 ];
12
13 if ( isset( $recipients[ $department ] ) ) {
14 $props = $contact_form->get_properties();
15 $props['mail']['recipient'] = $recipients[ $department ];
16 $contact_form->set_properties( $props );
17 }
18
19 return $contact_form;
20}

Методи get_properties() і set_properties() працюють із масивом налаштувань форми, включно з секцією mail. Змінювати можна не лише отримувача, а й тему (subject), тіло (body), додаткові заголовки (additional_headers) і відправника (sender).

Код розміщуйте там само, functions.php теми або через Code Snippets. Перед викочуванням на бойовий сайт протестуйте на staging: одрук у ключі масиву mail мовчки проігнорується, і лист піде на стандартну адресу.

Валідація полів перед надсиланням

CF7 дає фільтр wpcf7_validate, який спрацьовує до wpcf7_before_send_mail. Він приймає об’єкт WPCF7_Validation і дозволяє додати помилку, форма не піде, поки користувач не виправить:

1add_filter( 'wpcf7_validate_text*', 'cf7_custom_text_validation', 10, 2 );
2add_filter( 'wpcf7_validate_email*', 'cf7_custom_email_validation', 10, 2 );
3
4function cf7_custom_text_validation( $result, $tag ) {
5 $field_name = $tag->name;
6
7 if ( 'your-message' === $field_name ) {
8 $value = $_POST[ $field_name ] ?? '';
9
10 if ( mb_strlen( $value ) < 20 ) {
11 $result->invalidate( $tag, 'Сообщение должно быть не короче 20 символов.' );
12 }
13 }
14
15 return $result;
16}
17
18function cf7_custom_email_validation( $result, $tag ) {
19 $value = $_POST[ $tag->name ] ?? '';
20
21 if ( $value && ! str_contains( $value, '@' ) ) {
22 $result->invalidate( $tag, 'Укажите корректный email.' );
23 }
24
25 return $result;
26}

Фільтр іменується за схемою wpcf7_validate_<тип поля>: text* для обов’язкових текстових, email* для email, textarea* для текстових областей. Без зірочки, для необов’язкових полів. $tag, об’єкт із параметрами поля форми, включно з name.

Помилка додається методом invalidate($tag, 'текст ошибки'). Текст довільний, виводиться під полем форми. Для AJAX-надсилання (за замовчуванням у CF7) повідомлення з’являється без перезавантаження сторінки.

⁉️🤔 Часті запитання

У чому різниця між wpcf7_before_send_mail і wpcf7_mail_sent?

wpcf7_before_send_mail спрацьовує до надсилання листа, у ньому можна скасувати лист, змінити отримувача, модифікувати дані. wpcf7_mail_sent, екшн після успішного надсилання. Використовуйте його для логування, запуску вебхуків або запису в базу, коли лист уже гарантовано пішов. Якщо вам потрібно перервати процес, тільки before_send_mail.

Чи можна підключитися до хука без правки functions.php?

Так, через плагін Code Snippets (безплатний, у репозиторії WordPress). Він дає інтерфейс для додавання PHP-сніпетів без торкання теми. Сніпети можна вмикати й вимикати поштучно, зручно для налагодження. Альтернатива: WPCode (колишній Insert Headers and Footers), теж безплатний і з контролем scope, сніпет можна прив’язати до конкретної форми через conditional logic.

Як перевірити, що хук узагалі спрацьовує?

Найпростіший спосіб: error_log( 'HOOK FIRED' ) усередині callback-функції та перегляд wp-content/debug.log при ввімкненому WP_DEBUG. Для швидкого налагодження без логів тимчасово підмініть return $contact_form на wp_die('Хук работает'). На бойовому сайті так не робіть, тільки на локальному або staging-середовищі.

Що робити, якщо після оновлення CF7 код перестав працювати?

Першим ділом звірте сигнатуру хука. У версії 5.2 кількість аргументів wpcf7_before_send_mail зросла з 1 до 3. Якщо ваш callback оголошений без параметра $submission, додайте. Друге: перевірте, чи повертає функція об’єкт $contact_form. Третє: відкрийте лог помилок PHP, CF7 мовчки ігнорує фатальні помилки всередині хуків, форма надсилається як звичайно, а ви не бачите проблеми.

Чи є спосіб підписатися на кілька форм одним хуком?

Так, wpcf7_before_send_mail спрацьовує для всіх форм. Усередині callback фільтруйте за ID: $contact_form->id() поверне число, що збігається з ID шорткоду форми (наприклад, [contact-form-7 id="42"]). Порівняйте з потрібними ID і виконуйте різну логіку через if або switch.

Що ставити у функції теми, а що винести в плагін

Хуки CF7 технічно працюють із будь-якого місця: functions.php, Code Snippets, MU-плагін. Але є практичний розклад, який зекономить нерви при зміні теми або оновленні.

Код, зав’язаний на бізнес-логіку (зміна отримувача, інтеграція з CRM, збереження в базу), виносьте в окремий плагін або Code Snippets. Причина проста: зміна теми не повинна ламати маршрутизацію клієнтських листів. Валідацію полів, яка часто зав’язана на верстку та класи теми, тримайте в functions.php, вона втратить сенс при зміні теми в будь-якому разі.

Сніпети обсягом понад 30 рядків оформлюйте в MU-плагін (wp-content/mu-plugins/cf7-custom.php). MU-плагіни не вимикаються з адмінки, виконуються до звичайних плагінів і не потребують активації, просто поклали файл, і код працює.

Для геть ізольованих кейсів (одна форма, один сайт), functions.php + дочірня тема. Менше файлів, менше плутанини.

Ці самі принципи застосовні до будь-якого іншого плагіна форм, будь то WPForms, Gravity Forms або Fluent Forms. Хуки в кожного свої, але архітектурний підхід «бізнес-логіка окремо, презентаційна логіка з темою» універсальний.

На відео, покроковий розбір кастомної валідації полів CF7 із живим прикладом в адмінці WordPress. Підхід сумісний з описаним вище фільтром wpcf7_validate, тож дивіться як наочне доповнення до коду з розділу валідації.