
🚀 Cómo engancharse a Contact Form 7 antes del envío del formulario
Contact Form 7 está instalado en millones de sitios WordPress. Es simple, confiable y gratuito. Pero en cuanto el comportamiento estándar se queda corto y usted necesita validar un campo, establecer un destinatario dinámico o cancelar un correo según una condición, entra en el terreno del «¿cómo se hace esto?».
La documentación de CF7 describe los hooks brevemente. Los foros y Stack Overflow están llenos de fragmentos para versiones antiguas del plugin, y el código de hace cinco años falla silenciosamente en el CF7 moderno. Veamos qué hooks están vigentes hoy y cómo engancharse a ellos sin sorpresas.
💡 Resumen rápido:
- el hook principal es
wpcf7_before_send_mail, el punto de entrada para cualquier lógica previa al envío. - usted puede omitir el correo usando el filtro
wpcf7_skip_mailo una sola líneaskip_mail: onen los ajustes del formulario. - para obtener los datos enviados, use el objeto
$submissiony el métodoget_posted_data(). - para cambiar el destinatario dinámicamente, use
set_properties()en el objeto del formulario. - para la validación de campos, use
wpcf7_validatecon un filtro por tipo de campo.
Por qué necesita hooks previos al envío
El escenario estándar de CF7 es así: un usuario completa los campos, hace clic en «Enviar», el plugin envía un correo a una dirección fija. Eso es todo. Añadir lógica a este proceso sin hooks es imposible.
El hook wpcf7_before_send_mail y los filtros relacionados le permiten intervenir en la cadena exactamente en el momento en que los datos han sido validados pero el correo aún no se ha enviado. Usted puede:
- verificar una condición personalizada y cancelar el envío;
- guardar los datos del formulario en una base de datos o API externa;
- cambiar el destinatario del correo sobre la marcha;
- modificar el contenido del correo;
- activar una integración con un CRM o servicio de mailing.
Todos los ejemplos a continuación funcionan con Contact Form 7 versión 5.2 y superiores. Si su plugin es más antiguo, actualícelo primero: las firmas de los hooks antiguos son incompatibles.
Wpcf7_before_send_mail, el punto de entrada
A partir de la versión 5.2, la firma del hook cambió. Antes solo se pasaba el objeto del formulario; ahora hay tres argumentos:
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 object 5 // $abort — abort flag (true/false) 6 // $submission — WPCF7_Submission object with data 7 8 // your logic here 9 10 return $contact_form; 11 }
Archivo para colocar el código: functions.php de su tema activo o el plugin Code Snippets. La prioridad 10 funciona para la mayoría de las tareas; súbala a 1 si su callback necesita ejecutarse antes que otros, o bájela a 99 si debe ejecutarse después.
Tenga en cuenta que este hook es un filtro, no una acción. Usted debe devolver el objeto $contact_form (o WPCF7_ContactForm), de lo contrario la cadena se romperá incorrectamente.
Omitir el envío de correo
El caso de uso más común: no enviar una notificación si un campo se completa de una manera específica. Hay dos enfoques.
Usar el filtro wpcf7_skip_mail
Breve y limpio. El filtro acepta un valor booleano y el objeto del formulario:
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 // do not send email if field your-reason equals 'test' 10 if ( 'test' === ( $data['your-reason'] ?? '' ) ) { 11 $skip_mail = true; 12 } 13 } 14 15 return $skip_mail; 16 }
El método WPCF7_Submission::get_instance() proporciona acceso a los datos del envío dentro de este filtro. Sin él, get_posted_data() no está disponible ya que $submission no se pasa directamente a wpcf7_skip_mail.
Usar Ajustes adicionales
Si la lógica es lo más simple posible (omitir siempre el correo para un formulario específico), vaya al panel de administración. En la pestaña «Ajustes adicionales» del formulario, añada una línea:
1 skip_mail: on
No se requiere código. El plugin detiene el envío del correo justo después de la validación y el usuario ve un mensaje de éxito. Perfecto para formularios de demostración y entornos de prueba.
Una alternativa con el mismo efecto: demo_mode: on. La diferencia es que demo_mode simula completamente el éxito sin tocar el subsistema de correo, mientras que skip_mail solo omite el paso de envío, conservando todo el resto del comportamiento.
Obtener los datos enviados
El objeto $submission (el tercer argumento de wpcf7_before_send_mail) da acceso completo a lo que el usuario envió:
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 // all fields at once 5 $posted = $submission->get_posted_data(); 6 7 // specific field (key — name attribute of the tag in the form) 8 $user_name = $submission->get_posted_data( 'your-name' ); 9 $user_email = $submission->get_posted_data( 'your-email' ); 10 11 // ID of the post from which the form was sent 12 $post_id = $submission->get_meta( 'container_post_id' ); 13 14 // form ID 15 $form_id = $contact_form->id(); 16 17 // save to log or external service 18 if ( $user_email ) { 19 // for example, add subscriber to mailing list 20 } 21 22 return $contact_form; 23 }
Las claves de campo ('your-name', 'your-email') son los valores del atributo name en los shortcodes del formulario. Las etiquetas estándar como [text* your-name], [email* your-email] producen claves sin prefijos; simplemente copie lo que va después del espacio.
Importante: get_posted_data() devuelve datos en bruto antes del procesamiento por las plantillas de correo. Caracteres especiales, saltos de línea, todo tal como el usuario lo ingresó. Saneé antes de guardar en la base de datos usando sanitize_text_field() o funciones similares.
Cambio dinámico de destinatario
Suponga que el correo debe ir a diferentes gestores según el tema seleccionado en el formulario. Cambiamos la propiedad recipient sobre la marcha:
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 }
Los métodos get_properties() y set_properties() trabajan con el array de ajustes del formulario, incluyendo la sección mail. Usted puede cambiar no solo el destinatario, sino también el asunto (subject), cuerpo (body), cabeceras adicionales (additional_headers) y remitente (sender).
Coloque el código en la misma ubicación: el functions.php de su tema o mediante Code Snippets. Pruebe en staging antes de desplegar en producción: un error tipográfico en la clave del array mail se ignorará silenciosamente y el correo irá a la dirección predeterminada.
Validación de campos antes del envío
CF7 proporciona el filtro wpcf7_validate, que se dispara antes de wpcf7_before_send_mail. Recibe un objeto WPCF7_Validation y le permite añadir un error; el formulario no se enviará hasta que el usuario lo corrija:
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, 'Message must be at least 20 characters long.' ); 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, 'Enter a valid email.' ); 23 } 24 25 return $result; 26 }
El filtro se nombra siguiendo el patrón wpcf7_validate_<field type>: text* para campos de texto obligatorios, email* para email, textarea* para áreas de texto. Sin el asterisco, se aplica a campos opcionales. $tag es un objeto con los parámetros del campo del formulario, incluyendo name.
Los errores se añaden usando el método invalidate($tag, 'error text'). El texto es arbitrario y se muestra debajo del campo del formulario. Para el envío AJAX (el predeterminado en CF7), el mensaje aparece sin recargar la página.
⁉️🤔 Preguntas frecuentes
¿Cuál es la diferencia entre wpcf7_before_send_mail y wpcf7_mail_sent?
wpcf7_before_send_mailse dispara antes de que el correo se envíe; usted puede cancelar el correo, cambiar el destinatario o modificar los datos en él.wpcf7_mail_sentes una acción que se dispara después del envío exitoso. Úsela para registro, activación de webhooks o escritura en base de datos cuando el correo está garantizado que se ha enviado. Si necesita interrumpir el proceso, solobefore_send_mailfuncionará.
¿Puedo engancharme sin editar functions.php?
Sí, mediante el plugin Code Snippets (gratuito, en el repositorio de WordPress). Proporciona una interfaz para añadir fragmentos PHP sin tocar el tema. Los fragmentos se pueden activar y desactivar individualmente, lo cual es conveniente para la depuración. Una alternativa es WPCode (anteriormente Insert Headers and Footers), también gratuito y con control de alcance; usted puede vincular un fragmento a un formulario específico mediante lógica condicional.
¿Cómo verifico que el hook realmente se está disparando?
La forma más simple:
error_log( 'HOOK FIRED' )dentro de la función callback y revisarwp-content/debug.logconWP_DEBUGhabilitado. Para una depuración rápida sin registros, reemplace temporalmentereturn $contact_formconwp_die('Hook works'). No haga esto en un sitio de producción; solo en entornos locales o de staging.
¿Qué debo hacer si mi código deja de funcionar después de una actualización de CF7?
Primero, verifique la firma del hook. En la versión 5.2, el número de argumentos para
wpcf7_before_send_mailaumentó de 1 a 3. Si su callback está declarado sin el parámetro$submission, añádalo. Segundo, verifique que su función devuelva el objeto$contact_form. Tercero, revise el registro de errores de PHP; CF7 ignora silenciosamente los errores fatales dentro de los hooks, el formulario se envía como de costumbre y usted no ve el problema.
¿Hay alguna forma de suscribirse a múltiples formularios con un solo hook?
Sí,
wpcf7_before_send_mailse dispara para todos los formularios. Dentro del callback, filtre por ID:$contact_form->id()devuelve un número que coincide con el ID del shortcode del formulario (por ejemplo,[contact-form-7 id="42"]). Compárelo con los IDs necesarios y ejecute lógica diferente medianteifoswitch.
Qué poner en las funciones del tema vs. extraer a un plugin
Los hooks de CF7 técnicamente funcionan desde cualquier lugar: functions.php, Code Snippets, MU-plugin. Pero hay una división práctica que le ahorrará dolores de cabeza al cambiar temas o actualizar.
El código vinculado a la lógica de negocio (cambio de destinatario, integración CRM, guardado en base de datos) debe ir en un plugin separado o en Code Snippets. La razón es simple: cambiar de tema no debería romper el enrutamiento del correo del cliente. La validación de campos, que a menudo está ligada al diseño y las clases del tema, puede permanecer en functions.php; perderá su sentido cuando el tema cambie de todos modos.
Los fragmentos de más de 30 líneas deben formatearse como un MU-plugin (wp-content/mu-plugins/cf7-custom.php). Los MU-plugins no se pueden desactivar desde el panel de administración, se ejecutan antes que los plugins regulares y no requieren activación; simplemente coloque el archivo y el código funciona.
Para casos completamente aislados (un formulario, un sitio), use functions.php más un tema hijo. Menos archivos, menos confusión.
Estos mismos principios se aplican a cualquier otro plugin de formularios, ya sea WPForms, Gravity Forms o Fluent Forms. Cada uno tiene sus propios hooks, pero el enfoque arquitectónico de «lógica de negocio separada, lógica de presentación con el tema» es universal.
El video proporciona un desglose paso a paso de la validación personalizada de campos de CF7 con un ejemplo en vivo en el panel de administración de WordPress. El enfoque es compatible con el filtro wpcf7_validate descrito anteriormente, así que véalo como un complemento visual al código de la sección de validación.



