
🚀 Как подключиться к 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, так что смотрите как наглядное дополнение к коду из раздела валидации.



