
🚀 Як підключитися до 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 сигнатура хука змінилася. Раніше передавався тільки об’єкт форми, тепер три аргументи:
1 add_filter( 'wpcf7_before_send_mail', 'cf7_before_send_handler', 10, 3 ); 2 3 function 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
Коротко й чисто. Фільтр приймає булеве значення та об’єкт форми:
1 add_filter( 'wpcf7_skip_mail', 'maybe_skip_mail', 10, 2 ); 2 3 function 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), один рядок:
1 skip_mail: on
Жодного коду. Плагін перериває надсилання листа одразу після валідації, а користувач бачить повідомлення про успішне надсилання. Для демоформ і тестових середовищ, ідеально.
Альтернатива з тим самим ефектом: demo_mode: on. Різниця в тому, що demo_mode повністю симулює успіх без торкання поштової підсистеми, а skip_mail пропускає тільки крок надсилання, зберігаючи всю решту поведінки.
Отримання введених даних
Об’єкт $submission (третій аргумент wpcf7_before_send_mail) дає повний доступ до того, що надіслав користувач:
1 add_filter( 'wpcf7_before_send_mail', 'cf7_read_form_data', 10, 3 ); 2 3 function 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 на льоту:
1 add_filter( 'wpcf7_before_send_mail', 'cf7_dynamic_recipient', 10, 3 ); 2 3 function 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 і дозволяє додати помилку, форма не піде, поки користувач не виправить:
1 add_filter( 'wpcf7_validate_text*', 'cf7_custom_text_validation', 10, 2 ); 2 add_filter( 'wpcf7_validate_email*', 'cf7_custom_email_validation', 10, 2 ); 3 4 function 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 18 function 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, тож дивіться як наочне доповнення до коду з розділу валідації.



