Développer un nouveau type de champ de formulaire Contact Form 7

L’extension WordPress CF7 propose par défaut une quinzaine de types de champs différents permettant de créer des formulaires divers et variés. Mais saviez-vous qu’il est possible de créer son propre type de champ personnalisé pour Contact Form 7 ?

CF7 permet une grande flexibilité dans le développement d’un formulaire WordPress. Avec plus de 5 millions d’installations actives, il reste la référence en terme de création de formulaires dans WordPress.

Nombreux sont les addons CF7 nous aidant à profiter d’autres types de champs. Mais voyons ici comment développer notre propre tag CF7 [wc_order] maison pour :

  1. afficher un champ de type texte que l’on pourra pré-remplir via un paramètre d’URL $_GET
  2. valider la valeur de ce champ avant l’envoi du formulaire CF7 pour n’accepter que des numéros de commandes WooCommerce valides
  3. avoir la possibilité d’éditer les 2 messages d’erreurs possibles (si le champ est vide ou si le numéro de commande n’est pas valide) dans l’interface d’administration du formulaire CF7

Information

Le code complet de ce tutoriel est centralisé dans un plugin WordPress librement téléchargeable sur ce lien.

Objectif du tutoriel

Imaginons qu’un de vos clients gère une boutique en ligne propulsée par WooCommerce. Il souhaite proposer à ses acheteurs un formulaire de contact leur permettant de laisser une réclamation/remarque sur une commande passée.

Démo du nouveau champ ajouté et de la sauvegarde sur une commande WooCommerce

Le formulaire créé permet donc simplement d’afficher un champ pour récupérer le numéro de commande du client et une zone de texte pour accueillir le message à transmettre.

On répondra à ce besoin en créant un formulaire avec Contact Form 7 et en développant une nouvelle balise CF7 [wc_order] pour le champ de numéro de commande WooCommerce.

Son code CF7 est le suivant :

<label>Your WooCommerce order number:
    [wc_order* your-order-number] </label>

<label>Leave your feedback:
    [textarea your-feedback] </label>

[submit "Send"]

Marche à suivre

Bien que la documentation officielle propose un article à ce sujet, le meilleur moyen de comprendre comment enregistrer son propre tag de formulaire CF7 reste d’éplucher le code du plugin.

Ce tutoriel reprend donc les grandes lignes du fichier modules/text.php de l’extension qui a la responsabilité de créer le tag [text], [url], [email] et [tel] (des champs de type <input type="text" />).

Comment créer son propre champ CF7 ?

Enregistrer le nouveau tag avec wpcf7_add_form_tag()

On se hooke sur l’action wpcf7_init exécutée au début du cycle de vie du plugin. A ce moment, on va indiquer à CF7 que l’on souhaite enregistrer une nouvelle balise personnalisée de formulaire [wc_order] en utilisant la fonction wpcf7_add_form_tag().

<?php function register_cf7_wc_order_tag() {
	wpcf7_add_form_tag(
		[ 'wc_order', 'wc_order*' ],
		__NAMESPACE__ . '\\output_cf7_wc_order_tag',
		[ 'name-attr' => true ]
	);
}
add_action( 'wpcf7_init', __NAMESPACE__ . '\\register_cf7_wc_order_tag', 10, 0 );

Cette fonction accepte trois paramètres (deux requis et un facultatif).

Paramètre n°1 : la ou les nouvelle(s) balise(s) à créer

Le tableau passé en premier paramètre indique toutes les balises que vous souhaitez enregistrer pour générer votre nouveau champ. Dans notre cas, on enregistre à la fois [wc_order] et [wc_order*], le second imposant une valeur dans le champ obligatoire grâce à l’astérisque en fin de tag.

Attention

Vous ne pouvez utiliser que des lettres minuscules (a-z), des chiffres (0-9), des underscores (_) et des astérisques (*) pour le tag de votre nouveau champ.

Paramètre n°2 : la fonction de callback

La chaîne de caractère output_cf7_wc_order_tag passée en second paramètre ci-dessus indique la fonction qui va nous permettre :

  • d’analyser le tag et ses paramètres,
  • de générer le code HTML utilisé en front-end dans le formulaire CF7.

Cette fonction sera détaillée un peu plus loin dans ce tutoriel.

Paramètre n°3 : le tableau d’options

Les features définies dans le troisième tableau de paramètre de wpcf7_add_form_tag() indiquent certains propriétés de notre nouveau tag. Dans notre cas, le support de name-attr signifie que notre tag doit pouvoir prendre en charge l’attribut name pour générer un input unique.

En analysant le code de CF7, on remarque les principales features suivantes :

  • name-attr pour indiquer à CF7 que notre tag génèrera un input unique nécessitant un attribut name pour l’identifier
  • selectable-values indique que le tag proposera une liste de valeurs disponibles à la sélection de l’utilisateur
  • not-for-mail pour indiquer que la valeur d’un champ ne servira pas à construire le contenu de l’e-mail envoyé
  • do-not-store pour indiquer à Flamingo que la valeur d’un champ ne doit pas être sauvegardée en base de données

Générer le code HTML du champ de formulaire CF7

Maintenant que notre nouveau tag de formulaire CF7 est enregistré dans le système, il faut indiquer au plugin comment l’interpréter pour générer son markup HTML.

En effet, CF7 propose un moyen intelligent de définir des options sur un tag de formulaire pour nous permettre d’indiquer son id, un placeholder voire même son paramètre size. Notre fonction de callback va donc analyser ces différentes options pour générer le code HTML adéquat équivalent.

<?php function output_cf7_wc_order_tag( $tag ) {
	if ( empty( $tag->name ) ) {
		return '';
	}

	$validation_error = wpcf7_get_validation_error( $tag->name );
	$class            = wpcf7_form_controls_class( $tag->type, 'wpcf7-wc_order' );
	$atts             = [];
	$value            = (string) reset( $tag->values );

	if ( $validation_error ) {
		$class .= ' wpcf7-not-valid';
	}

	$atts['type']          = 'text';
	$atts['name']          = $tag->name;
	$atts['class']         = $tag->get_class_option( $class );
	$atts['id']            = $tag->get_id_option();
	$atts['tabindex']      = $tag->get_option( 'tabindex', 'signed_int', true );
	$atts['autocomplete']  = $tag->get_option( 'autocomplete', '[-0-9a-zA-Z]+', true );
	$atts['aria-invalid']  = $validation_error ? 'true' : 'false';
	$atts['size']          = $tag->get_size_option( '10' );
	$atts['maxlength']     = $tag->get_maxlength_option();
	$atts['minlength']     = $tag->get_minlength_option();

	if ( $tag->has_option( 'readonly' ) ) {
		$atts['readonly'] = 'readonly';
	}

	if ( $tag->is_required() ) {
		$atts['aria-required'] = 'true';
	}

	if ( $tag->has_option( 'placeholder' ) || $tag->has_option( 'watermark' ) ) {
		$atts['placeholder'] = $value;
		$value               = '';
	}

	$value = $tag->get_default_option( $value );
	$value = wpcf7_get_hangover( $tag->name, $value );

	if ( isset( $_GET['wc_order' ] ) && ! empty( $_GET['wc_order' ]) ) {
		$value = (int) $_GET['wc_order' ];
	}

	$atts['value'] = $value;
	$atts          = wpcf7_format_atts( $atts );

	$html = sprintf(
		'<span class="wpcf7-form-control-wrap %1$s"><input %2$s />%3$s</span>',
		sanitize_html_class( $tag->name ),
		$atts,
		$validation_error
	);

	return $html;
}

En sortie, toute fonction callback doit retourner une chaîne de caractères qui servira de code HTML de notre champ dans un formulaire CF7.

Information

Notez les lignes 42 à 44 où l’on récupère le paramètre wc_order de l’URL pour pré-remplir le champ d’une valeur par défaut. Cela pourra permettre de créer des liens automatiques avec le numéro de commande directement passé dans l’URL.

Notre fonction output_cf7_wc_order_tag() ci-dessus va recevoir ce $tag en paramètre entrant. C’est une instance de la classe WPCF7_FormTag.

Un tag décrit avec [wc_order* your-order-number placeholder id:mon_id class:ma_classe size:50 "Your order number"] offrira en paramètre entrant un $tag de type :

WPCF7_FormTag Object(
    [type] => wc_order*
    [basetype] => wc_order
    [name] => your-order-number
    [options] => Array(
		[0] => placeholder
		[1] => id:mon_id
		[2] => class:ma_classe
		[3] => size:50
	)
    [raw_values] => Array(
        [0] => Your order number
    )
    [values] => Array(
        [0] => Your order number
    )
    [labels] => Array(
        [0] => Your order number
    )
    [attr] => 
    [content] => 
)

Valider la valeur du champ avec le filtre wpcf7_validate

CF7 propose des filtres dynamiques wpcf7_validate_{$tag} nous offrant un moyen d’exécuter du code maison pour valider notre champ de formulaire CF7 en AJAX.

On pourra ainsi récupérer la valeur du champ via $_POST[ $tag->name ] et effectuer n’importe quelle logique de validation.

<?php function validate_wc_order_input_value( $result, $tag ) {
	$name = $tag->name;

	if ( $tag->basetype !== 'wc_order' ) {
		return $result;
	}

	$value = isset( $_POST[ $name ] ) ? trim( wp_unslash( strtr( (string) $_POST[$name], "\n", ' ' ) ) ) : '';
	$value = strtoupper( $value );

	// Check if value is empty?
	if ( $tag->is_required() && empty( $value ) ) {
		$result->invalidate( $tag, wpcf7_get_message( 'wc_order_field_required' ) );
	}

	// Check if we're dealing with a correct WooCommerce Order?
	if ( ! $order = wc_get_order( $value ) ) {
		$result->invalidate( $tag, wpcf7_get_message( 'wc_order_field_invalid' ) );
	}

	return $result;
}
add_filter( 'wpcf7_validate_wc_order', __NAMESPACE__ . '\\validate_wc_order_input_value', 10, 2 );
add_filter( 'wpcf7_validate_wc_order*', __NAMESPACE__ . '\\validate_wc_order_input_value', 10, 2 );

Dans notre code ci-dessus, on va :

  • s’assurer d’avoir une valeur,
  • mais aussi vérifier que la valeur correspond à un numéro de commande WooCommerce valide.

Dans le cas d’une valeur incorrecte, on appelle la méthode $result->invalidate() en fournissant le tag concerné et le message d’erreur à afficher. Ce message est fourni par wpcf7_get_message() car il est dynamique et éditable dans le back-office du formulaire concerné.

Ajouter de nouveaux messages d’erreur avec le filtre wpcf7_messages

Le filtre wpcf7_messages nous offre un excellent moyen de charger des nouveaux champs dans l’onglet « Messages » de configuration du formulaire CF7.

On va donc créer deux nouveaux champs pour pouvoir offrir à l’administrateur la possibilité de modifier les messages d’erreur affichés lors de la validation précédemment décrite.

On retrouve, en clé de tableau, l’identifiant utilisé précédemment. Notre tableau contient deux valeurs :

  1. description est la petite phrase présente au-dessus du champ dans l’administration, permettant de comprendre l’utilité du message
  2. default est la valeur par défaut qui sera utilisée si l’administrateur n’a pas modifié le message en question
<?php function add_new_error_messages_for_wc_order_cf7_field( $messages ) {
	$messages = array_merge(
		$messages,
		[
			'wc_order_field_required' => [
				'description' => __( 'The order number is missing.', 'cf7wcp' ),
				'default'     => __( 'You need to provide your order number.', 'cf7wcp' ),
			],
			'wc_order_field_invalid' => [
				'description' => __( 'The selected order number is not valid.', 'cf7wcp' ),
				'default'     => __( 'Your order number is not valid.', 'cf7wcp' ),
			],
		]
	);

	return $messages;
}
add_filter( 'wpcf7_messages', __NAMESPACE__ . '\\add_new_error_messages_for_wc_order_cf7_field', 10, 1 );

Grâce au code ci-dessus, on pourra alors référencer le premier message d’erreur avec wpcf7_get_message( 'wc_order_field_required' ) et le second avec wpcf7_get_message( 'wc_order_field_invalid' ).

Contact Form 7 se chargera d’utiliser la valeur par défaut ou le nouveau message défini dans l’administration du formulaire.

Avec tout ça, nous avons déjà créé un nouveau type de champ de formulaire Contact Form 7 pour accueillir le numéro de commande WooCommerce d’un acheteur. Il nous suffit alors de mentionner [your-order-number] dans le corps du mail envoyé par CF7 pour récupérer sa valeur.

Ecrire une note sur la commande WooCommerce après l’envoi du formulaire CF7

Allons un peu plus loin et interceptons les données du formulaire CF7 envoyé. On va ajouter une note de commande WooCommerce contenant la remarque de l’acheteur : toutes les remarques seront ainsi centralisées dans les notes de la commande.

Le feedback de l’acheteur est stocké dans une note de commande
<?php function update_wc_order_meta_after_form_submission() {
	$submission = \WPCF7_Submission::get_instance();

	if ( ! $submission ) {
		return;
	}

	$posted_data = $submission->get_posted_data();
	$order_id    = (int) $posted_data['your-order-number'];
	$order       = wc_get_order( $order_id );
	$order->add_order_note( sanitize_textarea_field( $posted_data['your-feedback'] ) );
	$order->save();
}
add_action( 'wpcf7_mail_sent', __NAMESPACE__ . '\\update_wc_order_meta_after_form_submission' );
add_action( 'wpcf7_mail_failed', __NAMESPACE__ . '\\update_wc_order_meta_after_form_submission' );

Détaillons ce qui se passe dans cette fonction :

  • lignes 2 à 9 : on s’assure de récupérer l’objet WPCF7_Submission et stocke les données du formulaire envoyé dans un tableau $posted_data. On a donc accès à la clé your-order-number contentant la valeur de notre champ créé sur-mesure (l’identifiant de la commande WooCommerce de l’acheteur)
  • ligne 10 : on va récupérer l’objet WC_Order nous permettant d’intéragir avec la commande de notre base de données
  • ligne 11 et 12 : on ajoute une nouvelle note de commande avec add_order_note()

Enfin, cette logique est hookée aux 2 événements CF7 déclenchés après la soumission d’un formulaire :

  1. wpcf7_mail_sent lorsque le mail a bien été envoyé
  2. et wpcf7_mail_failed qui, à contrario, se déclenchera si le mail n’a pas pu être envoyé

Conclusion

Et voila le travail : un nouveau type de champ CF7 tout neuf ! N’hésitez pas à partager en commentaire vos créations sur-mesure 🙂


Vous avez aimé cet article ?

Partagez-le sur vos réseaux sociaux en guise de remerciement :)


Laisser un commentaire