Skip to content

Tudo para WordPress, desenvolvimento web — e não só

🚀 Como interligar-se ao Contact Form 7 antes do envio do formulário

🚀 Como interligar-se ao Contact Form 7 antes do envio do formulário

Contact Form 7 está instalado em milhões de sites WordPress. É simples, fiável e gratuito. Mas assim que o comportamento padrão se torna insuficiente e precisa de validar um campo, definir um destinatário dinâmico ou cancelar um email com base numa condição, entra no território do «como é que isto se faz ao certo».

A documentação do CF7 descreve os hooks de forma breve. Os fóruns e o Stack Overflow estão cheios de snippets para versões antigas do plugin, e código com cinco anos quebra silenciosamente no CF7 moderno. Vamos perceber quais são os hooks atuais e como ligar-se a eles sem surpresas.

💡 Visão geral rápida:

  • o hook principal é o wpcf7_before_send_mail, o ponto de entrada para qualquer lógica de pré-envio.
  • pode ignorar o envio do email usando o filtro wpcf7_skip_mail ou uma única linha skip_mail: on nas definições do formulário.
  • para obter os dados submetidos, utilize o objeto $submission e o método get_posted_data().
  • para alterar o destinatário dinamicamente, utilize set_properties() no objeto do formulário.
  • para validação de campos, utilize wpcf7_validate com um filtro por tipo de campo.

Por que precisa de hooks de pré-envio

O cenário padrão do CF7 é assim: um utilizador preenche os campos, clica em «Enviar», o plugin envia um email para um endereço fixo. É tudo. Adicionar lógica a este processo sem hooks é impossível.

O hook wpcf7_before_send_mail e os filtros relacionados permitem-lhe intervir na cadeia exatamente no momento em que os dados foram validados, mas o email ainda não foi enviado. Pode:

  • verificar uma condição personalizada e cancelar a submissão;
  • guardar os dados do formulário numa base de dados ou API externa;
  • alterar o destinatário do email dinamicamente;
  • modificar o conteúdo do email;
  • acionar uma integração com um CRM ou serviço de mailing.

Todos os exemplos abaixo funcionam com o Contact Form 7 versão 5.2 e superior. Se o seu plugin for mais antigo, atualize primeiro: as assinaturas dos hooks antigos são incompatíveis.

Wpcf7_before_send_mail, o ponto de entrada

A partir da versão 5.2, a assinatura do hook mudou. Anteriormente, apenas o objeto do formulário era passado; agora existem três argumentos:

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 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}

Ficheiro para colocação: functions.php do seu tema ativo ou o plugin Code Snippets. A prioridade 10 funciona para a maioria das tarefas; aumente para 1 se o seu callback precisar de ser executado antes dos outros, ou diminua para 99 se precisar de ser executado depois.

Note que este hook é um filtro, não uma ação. Deve retornar o objeto $contact_form (ou WPCF7_ContactForm), caso contrário a cadeia será interrompida incorretamente.

Ignorar o envio de email

O caso de uso mais comum: não enviar uma notificação se um campo for preenchido de uma forma específica. Existem duas abordagens.

Usar o filtro wpcf7_skip_mail

Curto e limpo. O filtro aceita um valor booleano e o objeto do formulário:

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 // 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}

O método WPCF7_Submission::get_instance() fornece acesso aos dados da submissão dentro deste filtro. Sem ele, get_posted_data() fica indisponível, uma vez que $submission não é passado diretamente para wpcf7_skip_mail.

Usar as Definições Adicionais

Se a lógica for a mais simples possível (ignorar sempre o email para um formulário específico), vá ao painel de administração. No separador «Definições Adicionais» do formulário, adicione uma linha:

1skip_mail: on

Não requer código. O plugin interrompe o envio do email logo após a validação e o utilizador vê uma mensagem de sucesso. Perfeito para formulários de demonstração e ambientes de teste.

Uma alternativa com o mesmo efeito: demo_mode: on. A diferença é que o demo_mode simula totalmente o sucesso sem tocar no subsistema de correio, enquanto o skip_mail apenas ignora o passo de envio, preservando todos os outros comportamentos.

Obter os dados submetidos

O objeto $submission (o terceiro argumento de wpcf7_before_send_mail) dá acesso total ao que o utilizador submeteu:

1add_filter( 'wpcf7_before_send_mail', 'cf7_read_form_data', 10, 3 );
2
3function 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}

As chaves dos campos ('your-name', 'your-email') são os valores do atributo name nos shortcodes do formulário. Tags padrão como [text* your-name], [email* your-email] produzem chaves sem prefixos; basta copiar o que vem depois do espaço.

Importante: get_posted_data() retorna os dados brutos antes do processamento pelos modelos de email. Caracteres especiais, quebras de linha, tudo como o utilizador introduziu. Higienize antes de guardar na base de dados usando sanitize_text_field() ou funções semelhantes.

Alteração dinâmica do destinatário

Suponha que o email deve ir para gestores diferentes dependendo do tópico selecionado no formulário. Alteramos a propriedade recipient dinamicamente:

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}

Os métodos get_properties() e set_properties() trabalham com o array de definições do formulário, incluindo a secção mail. Pode alterar não só o destinatário, mas também o assunto (subject), corpo (body), cabeçalhos adicionais (additional_headers) e remetente (sender).

Coloque o código no mesmo local: o functions.php do seu tema ou via Code Snippets. Teste em staging antes de implementar em produção: um erro de digitação na chave do array mail será ignorado silenciosamente e o email será enviado para o endereço padrão.

Validação de campos antes do envio

O CF7 fornece o filtro wpcf7_validate, que é acionado antes do wpcf7_before_send_mail. Ele recebe um objeto WPCF7_Validation e permite adicionar um erro; o formulário não será submetido até que o utilizador o corrija:

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, 'Message must be at least 20 characters long.' );
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, 'Enter a valid email.' );
23 }
24
25 return $result;
26}

O filtro é nomeado seguindo o padrão wpcf7_validate_<field type>: text* para campos de texto obrigatórios, email* para email, textarea* para áreas de texto. Sem o asterisco, aplica-se a campos opcionais. $tag é um objeto com os parâmetros do campo do formulário, incluindo name.

Os erros são adicionados usando o método invalidate($tag, 'error text'). O texto é arbitrário e é exibido abaixo do campo do formulário. Para submissão AJAX (o padrão no CF7), a mensagem aparece sem recarregar a página.

⁉️🤔 Perguntas frequentes

Qual é a diferença entre wpcf7_before_send_mail e wpcf7_mail_sent?

O wpcf7_before_send_mail é acionado antes do envio do email; pode cancelar o email, alterar o destinatário ou modificar os dados nele. O wpcf7_mail_sent é uma ação que é acionada depois do envio bem-sucedido. Utilize-o para registo de logs, acionar webhooks ou escritas na base de dados quando o envio do email é garantido. Se precisar de interromper o processo, apenas o before_send_mail funcionará.

Posso ligar-me a um hook sem editar o functions.php?

Sim, através do plugin Code Snippets (gratuito, no repositório do WordPress). Ele fornece uma interface para adicionar snippets PHP sem tocar no tema. Os snippets podem ser ativados e desativados individualmente, o que é conveniente para depuração. Uma alternativa é o WPCode (anteriormente Insert Headers and Footers), também gratuito e com controlo de âmbito; pode vincular um snippet a um formulário específico através de lógica condicional.

Como verifico se o hook está realmente a ser acionado?

A forma mais simples: error_log( 'HOOK FIRED' ) dentro da função de callback e verificar wp-content/debug.log com WP_DEBUG ativado. Para uma depuração rápida sem logs, substitua temporariamente return $contact_form por wp_die('Hook works'). Não faça isto num site de produção; apenas em ambientes locais ou de staging.

O que devo fazer se o meu código deixar de funcionar após uma atualização do CF7?

Primeiro, verifique a assinatura do hook. Na versão 5.2, o número de argumentos para wpcf7_before_send_mail aumentou de 1 para 3. Se o seu callback estiver declarado sem o parâmetro $submission, adicione-o. Segundo, verifique se a sua função retorna o objeto $contact_form. Terceiro, verifique o registo de erros do PHP; o CF7 ignora silenciosamente erros fatais dentro dos hooks, o formulário é submetido como habitualmente e não vê o problema.

Existe uma forma de subscrever vários formulários com um único hook?

Sim, o wpcf7_before_send_mail é acionado para todos os formulários. Dentro do callback, filtre por ID: $contact_form->id() retorna um número correspondente ao ID do shortcode do formulário (por exemplo, [contact-form-7 id="42"]). Compare-o com os IDs necessários e execute lógicas diferentes através de if ou switch.

O que colocar no functions do tema vs. extrair para um plugin

Os hooks do CF7 funcionam tecnicamente a partir de qualquer lugar: functions.php, Code Snippets, MU-plugin. Mas existe uma divisão prática que evitará dores de cabeça ao mudar de temas ou ao atualizar.

O código ligado à lógica de negócio (alteração de destinatário, integração com CRM, gravações na base de dados) deve ir para um plugin separado ou para o Code Snippets. A razão é simples: mudar de tema não deve quebrar o encaminhamento de emails do cliente. A validação de campos, que está frequentemente ligada ao layout e às classes do tema, pode permanecer no functions.php; perderá o sentido quando o tema mudar, de qualquer forma.

Snippets com mais de 30 linhas devem ser formatados como um MU-plugin (wp-content/mu-plugins/cf7-custom.php). Os MU-plugins não podem ser desativados a partir do painel de administração, são executados antes dos plugins regulares e não requerem ativação; basta colocar o ficheiro na pasta e o código funciona.

Para casos completamente isolados (um formulário, um site), utilize o functions.php juntamente com um tema filho. Menos ficheiros, menos confusão.

Estes mesmos princípios aplicam-se a qualquer outro plugin de formulários, seja ele o WPForms, o Gravity Forms ou o Fluent Forms. Cada um tem os seus próprios hooks, mas a abordagem arquitetural de «lógica de negócio separada, lógica de apresentação com o tema» é universal.

O vídeo fornece uma análise passo a passo da validação de campos personalizada do CF7 com um exemplo ao vivo no painel de administração do WordPress. A abordagem é compatível com o filtro wpcf7_validate descrito acima, por isso veja-o como um complemento visual ao código da secção de validação.