Préremplir les champs d’un formulaire à l’aide d’une chaîne de requête

Comment personnaliser ses formulaires et les rendre dynamiques ?

Voici un tutoriel complet sur la possibilité de préremplir certains champs de formulaire.

Ce dont on a besoin

Nous allons ici utiliser :

  • un custom post type
  • Un widget
  • un formulaire Ninja form
  • des champs additionnels ACF

Le scénario

Le scénario est le suivant. Nous sommes dans un article ou un custom post type « formation ». Il s’agit de cours informatiques, répartis par catégories et par niveaux. Chaque post doit contenir des dates et des lieux spécifiques. Ces informations sont des « champ additionnels » ou « cutom fields » crées à l’aide d’ACF.

Dans la sidebar, on a un widget qui affiche les formations à venir. On les regroupe par ville puis par date de début et de fin.

widget prochaines sessions

Ces dernières sont entourées d’un lien dynamique qui doit renvoyer vers notre formulaire, en pré-remplissant certains champs. Par exemple, si je suis dans un article dont le titre est « python expert », et que je clique sur le premier lien « Orléans, du 18/05/2020 au 22/50/2020 », mon formulaire affichera alors en en-tête le titre et les informations de ville et de dates, comme ci-dessous.

ninja form dynamic field

Lorsque l’utilisateur valide le formulaire, on reçoit alors un mail indiquant entre autre les préférences de lieu et de date.

C’est parti !

Le custom post type

Notre custom post formation a deux taxonomies, une pour les catégories de formation, une deuxième pour le niveau des formations. Je mets ici le code entier de mon CPT.

function cc_formation_cpt() {
/* Formation */
$labels = array(
'name' => _x('Formations', 'Post Type General Name', 'text_domain'),
'singular_name' => _x('Formation', 'Post Type Singular Name', 'text_domain'),
'menu_name' => _x('Formations', 'text_domain'),
'name_admin_bar' => _x('Formations', 'text_domain'),
'all_items' => _x('Toutes les formations', 'text_domain'),
'add_new_item' => _x('Ajouter une nouvelle formation', 'text_domain'),
'add_new' => _x('Ajouter', 'text_domain'),
'new_item' => _x('Nouvelle formation', 'text_domain' ),
'edit_item' => _x('Editer la formation', 'text_domain'),
'update_item' => _x('Mettre à jour la formation', 'text_domain'),
'view_item' => _x('Voir la formation', 'text_domain'),
'search_items' => _x('Rechercher une formation', 'text_domain'),
'not_found' => _x('Aucune formation trouvée', 'text_domain'),
'not_found_in_trash' => _x('Aucune formation trouvée dans la corbeille', 'text_domain'),
);

$args = array(
'label' => _x('Formation', 'text_domain'),
'description' => _x('Formations', 'text_domain'),
'labels' => $labels,
'supports' => array('title', 'editor', 'thumbnail', 'comments', 'revisions', 'custom-fields'),
'hierarchical' => false,
'public' => true,
'show_ui' => true,
'show_in_menu' => true,
'menu_position' => 5,
'menu_icon' => 'dashicons-admin-home',
'show_in_admin_bar' => true,
'show_in_nav_menus' => true,
'can_export' => true,
'has_archive' => false,
'exclude_from_search' => false,
'publicly_queryable' => true,
'query_var' => true,
'rewrite' => array( 'slug' => 'toutes-nos-formations' ),
'capability_type' => 'post',
'show_in_rest' =>'true',
);
register_post_type('formation', $args);
}
add_action('init', 'cc_formation_cpt', 10);



function cc_formation_category_taxonomies() {

$labels_cat_formation = array(
'name' => _x( 'Catégories de la formation', 'taxonomy general name'),
'singular_name' => _x( 'Catégories de la formation', 'taxonomy singular name'),
'search_items' => __( 'Rechercher une catégorie'),
'popular_items' => __( 'Catégories populaires'),
'all_items' => __( 'Toutes les catégories'),
'edit_item' => __( 'Editer une catégorie'),
'update_item' => __( 'Mettre à jour une catégorie'),
'add_new_item' => __( 'Ajouter une nouvelle catégorie'),
'new_item_name' => __( 'Nom de la nouvelle catégorie'),
'add_or_remove_items' => __( 'Ajouter ou supprimer une catégorie'),
'choose_from_most_used' => __( 'Choisir parmi les catégories les plus utilisées'),
'not_found' => __( 'Pas de catégories trouvée'),
'menu_name' => __( 'Catégorie de formation'),
);

$args_cat_formation = array(
'hierarchical' => true,
'labels' => $labels_cat_formation,
'show_ui' => true,
'show_in_rest' => true,
'show_admin_column' => true,
'query_var' => true,
'rewrite' => array( 'slug' => 'categories-formations' ),
);

register_taxonomy( 'categories-formations', 'formation', $args_cat_formation );
}
add_action( 'init', 'cc_formation_category_taxonomies', 0 );

function cc_formation_level_taxonomies() {

$labels_level_formation = array(
'name' => _x( 'Niveaux de la formation', 'taxonomy general name'),
'singular_name' => _x( 'Niveau de la formation', 'taxonomy singular name'),
'search_items' => __( 'Rechercher un niveau'),
'popular_items' => __( 'Niveaux populaires'),
'all_items' => __( 'Tous les Niveaux'),
'edit_item' => __( 'Editer un niveau'),
'update_item' => __( 'Mettre à jour un niveau'),
'add_new_item' => __( 'Ajouter un nouveau niveau'),
'new_item_name' => __( 'Nom de le nouveau niveau'),
'add_or_remove_items' => __( 'Ajouter ou supprimer un niveau'),
'choose_from_most_used' => __( 'Choisir parmi les nivexau les plus utilisées'),
'not_found' => __( 'Pas de niveau trouvé'),
'menu_name' => __( 'Niveau de formation'),
);

$args_level_formation = array(

'hierarchical' => true,
'labels' => $labels_level_formation,
'show_ui' => true,
'show_in_rest' => true,
'show_admin_column' => true,
'query_var' => true,
'rewrite' => array( 'slug' => 'niveau-formations' ),
);

register_taxonomy( 'niveau-formations', 'formation', $args_level_formation );
}
add_action( 'init', 'cc_formation_level_taxonomies', 0 );

Les champs ACF

Depuis le backoffice et les options d’Acf, je rajoute mes trois champs ville, date de début et date de fin.

acf fields

Champ ville ou location

Pour le champ « ville », je choisi un champ select et renseigne les différentes options.

champ acf ville

Champ date

Pour le champ « date_de_debut » et « date_de_fin« , je choisir le type « date » et son format.

champ date acf

Enfin, je précise les conditions d’affichage : ici le « type de publication » doit être égal à « Formation » (le nom de mon custom post type.)

condition acf

Dans mon projet, je n’affiche pas ces champs dans le fichier single-formation. Ces articles ne doivent pas contenir de dates ni de lieu attitrés. Mais ces informations sont bien enregistrées en base de donnée au moment de l’édition du CPT. On va donc pouvoir les récupérer et les afficher ailleurs.

Le widget des « formations à venir »

Voici le code complet de mon widget.

	class Cc_Upcoming_Events extends WP_Widget {
function __construct() {
parent::__construct(
'cc_upcoming_events', // Base ID
__( 'Cc Upcoming Events', 'text_domain' ), // Name
array( 'description' => __( 'Shows Formations dates', 'text_domain' ), ) // Args
);
}



public function form( $instance ) {
$tax_id = '';
if( !empty( $instance['tax_id'] ) ) {
$tax_id = absint( $instance['tax_id'] ) ;
}
$niveau_id = '';
if( !empty( $instance['niveau_id'] ) ) {
$niveau_id = absint( $instance['niveau_id'] ) ;
}
$widget_defaults = array(
'title' => 'Upcoming Events',
'number_events' => 5
);

$instance = wp_parse_args( (array) $instance, $widget_defaults );?>
<p>
<label for="<?php echo $this->get_field_id( 'title' ); ?>"><?php _e( 'Title', 'text_domain' ); ?></label>
<input type="text" id="<?php echo $this->get_field_id( 'title' ); ?>" name="<?php echo $this->get_field_name( 'title' ); ?>" class="widefat" value="<?php echo esc_attr( $instance['title'] ); ?>">
</p>
<p>
<label for="<?php echo $this->get_field_id( 'number_events' ); ?>"><?php _e( 'Number of events to show', 'text_domain' ); ?></label>
<select id="<?php echo $this->get_field_id( 'number_events' ); ?>" name="<?php echo $this->get_field_name( 'number_events' ); ?>" class="widefat">
<?php for ( $i = 1; $i <= 10; $i++ ): ?>
<option value="<?php echo $i; ?>" <?php selected( $i, $instance['number_events'], true ); ?>><?php echo $i; ?></option>
<?php endfor; ?>
</select>
</p>
<p>
<label for="<?php echo $this->get_field_id('tax_id'); ?>"><?php _e( 'Taxonomy Name:' )?></label>
<select id="<?php echo $this->get_field_id('tax_id'); ?>" name="<?php echo $this->get_field_name('tax_id'); ?>">
<?php
$custom_terms = get_terms('categories-formations');

foreach($custom_terms as $custom_term) {
$selected = ( $custom_term->term_id == esc_attr( $tax_id ) ) ? ' selected = "selected" ' : '';
$option = '<option '.$selected .'value="' . $custom_term->term_id;
$option = $option .'">';
$option = $option .$custom_term->name;
$option = $option .'</option>';
echo $option;
}
?>
</select>

</p>

<p>
<label for="<?php echo $this->get_field_id('niveau_id'); ?>"><?php _e( 'Level Name:' )?></label>
<select id="<?php echo $this->get_field_id('niveau_id'); ?>" name="<?php echo $this->get_field_name('niveau_id'); ?>">
<?php
$custom_terms = get_terms('niveau-formations');

foreach($custom_terms as $custom_term) {
$selected = ( $custom_term->term_id == esc_attr( $niveau_id ) ) ? ' selected = "selected" ' : '';
$option = '<option '.$selected .'value="' . $custom_term->term_id;
$option = $option .'">';
$option = $option .$custom_term->name;
$option = $option .'</option>';
echo $option;
}
?>
</select>

</p>
<?php
}



public function update( $new_instance, $old_instance ) {
$instance = $old_instance;

$instance['title'] = $new_instance['title'];
$instance['number_events'] = $new_instance['number_events'];
$instance['tax_id'] = ( ! empty( $new_instance['tax_id'] ) ) ? strip_tags( $new_instance['tax_id'] ) : '';
$instance['niveau_id'] = ( ! empty( $new_instance['niveau_id'] ) ) ? strip_tags( $new_instance['niveau_id'] ) : '';
return $instance;
}



public function widget( $args, $instance ) {
echo $args['before_widget'];
extract( $args );
$title = apply_filters( 'widget_title', $instance['title'] );
$tax_id = absint( $instance['tax_id'] ) ;
$niveau_id = absint( $instance['niveau_id'] ) ;
$args = array(
'post_type' => 'formation',
'posts_per_page' => $instance['number_events'],
'tax_query' => array(
'relation' => 'AND',
array(
'taxonomy' => 'categories-formations',
'field' => 'term_id',
'terms' => $tax_id,
),
array(
'taxonomy' => 'niveau-formations',
'field' => 'term_id',
'terms' => $niveau_id,
),
),

);

$upcoming_events = new WP_Query($args);

if ( $title ) {
echo $before_title . $title . $after_title;
}
// vars
?>

<?php

$title = get_the_title();
// First, group the posts by the city.
$groups = array();
foreach ( $upcoming_events->posts as $i => $p ) {
$city = get_field( 'ville', $p->ID );

if ( ! isset( $groups[ $city ] ) ) {
$groups[ $city ] = array();
}
$groups[ $city ][] =& $upcoming_events->posts[ $i ];
}

// Optional, sort the city names.
ksort( $groups, SORT_FLAG_CASE );

// Then display the posts.
if ( ! empty( $groups ) ) {
echo '<div class="event_entries">';
foreach ( $groups as $city => $posts ) {
if ( ! empty( $posts ) ) {
// Display city name.
echo '<h3>' . $city . '</h3>';

// Display posts in that city.
echo '<ul>';
foreach ( $posts as $post ) {
$event_start_date = get_field( 'date_de_debut', $post->ID );
$event_end_date = get_field( 'date_de_fin', $post->ID );

echo '<li><a href="http://localhost/monsite/inscription/?titre='. $title . '&ville='. $city .'&date_de_debut=' . $event_start_date .'&date_de_fin=' . $event_end_date .'&niveau=' . $niveau_id .'&categorie=' . $tax_id . '"> Du ' . $event_start_date . ' au ' . $event_end_date . '</a></li>';
}
echo '</ul>';
}
}
echo '</div>'; // close .event_entries
}
?>


<?php


echo $after_widget;
}
}

Dans la fonction « form« , on a plusieurs champs select : un pour le nombre de post à afficher, un second pour ma taxonomie « catégorie », un dernier pour ma taxonomie « niveau ».

Pour pouvoir boucler sur notre CPT en fonction de ces deux taxonomies et des terms sélectionnés par l’utilisateur, on va créer la boucle avec une « multiple tax_query » comme expliqué dans le codex

	$args = array(
'post_type' => 'formation',
'posts_per_page' => $instance['number_events'],
'tax_query' => array(
'relation' => 'AND',
array(
'taxonomy' => 'categories-formations',
'field' => 'term_id',
'terms' => $tax_id,
),
array(
'taxonomy' => 'niveau-formations',
'field' => 'term_id',
'terms' => $niveau_id,
),
),

);

$upcoming_events = new WP_Query($args);

Le reste du code nous permet, à travers des boucles imbriquées, d’afficher les custom post type, en les regroupant par ville. On a ainsi une répartition avec la ville en titre, et tous les CPT enregistrés avec la même ville dans une liste juste en dessous.

Le lien dynamique et notre query string

Dans le dernier foreach, on récupère nos custom field « date_de_debut » et « date_de_fin« , et on construit le lien avec notre requête dynamique. Je récupère aussi le titre de l’article avec $title = get_the_title(); ,  et les champs ACF.

  foreach ( $posts as $post ) {
$event_start_date = get_field( 'date_de_debut', $post->ID );
$event_end_date = get_field( 'date_de_fin', $post->ID );

echo '<li><a href="http://localhost/monsite/inscription/?titre='. $title . '&ville='. $city .'&date_de_debut=' . $event_start_date .'&date_de_fin=' . $event_end_date .'&niveau=' . $niveau_id .'&categorie=' . $tax_id . '"> Du ' . $event_start_date . ' au ' . $event_end_date . '</a></li>';
}

 

Au besoin je récupère aussi les taxonomies choisies dans les champs select du widget.

Le formulaire : Ninja Form

Pour mon formulaire, j’utilise Ninja Form, mais d’autres proposent les mêmes fonctionnalités : wp_form, gravity form etc. C’est aussi possible avec Contact form 7 et le plugin contact form 7 dynamic text extension.

Avec la version gratuite de Ninja Form, construisez votre formulaire. Un tuto explique très bien comment créer et récupérer les données d’un lien dynamique dans Ninja Form.

Créer deux champs additionnels pour mes « query string »

Pour suivre mon exemple, je vais donc devoir créer deux champs additionnels : un pour afficher le titre du CPT de provenance, l’autre pour afficher les valeurs de mes champs ACF.

Je crée donc deux « lignes de texte« .

query ninja form fields

Querystring pour le titre du CPT

On efface les libellés, et dans les restrictions, on désactive la saisie. Si les restrictions ne s’affichent pas, il faut retourner dans les options du plugin et activer le mode « développeur ».

titre ninja disable field

Dans l’affichage, je définis comme valeur par défaut ma première query string : {querystring:titre}. Le champ va donc récupérer ici ma variable titre insérée plus tôt dans mon lien.

Si je suis dans une formation dont le titre est « python expert » par exemple, je le retrouverai donc dans mon formulaire. On peut ensuite le styliser, comme ci-dessous.

ninja form dynamic field

Dans les « noms de classe personnalisées« , j’ajoute mes classes qui vont justement me servir à styliser mon titre : enlever les bordures, changer la taille de police, modifier les marges etc.

Querystring pour la ville et les dates

Mon deuxième champ va me permettre de récupérer les informations de ville et de dates envoyés depuis mon widget.

ninja form hidden input 2

Ici aussi j’efface le libellé, je désactive la saisie. Je personnalise la valeur de ma chaîne de requête avec du texte additionnel. Je nomme mes tags exactement de la même manière que dans mon lien dynamique : {querystring:date_de_debut}, {querystring:date_de_fin} et {querystring:ville}. :

&ville=’. $city .’&date_de_debut=’ . $event_start_date .’&date_de_fin=’ . $event_end_date

Un autre affichage

Si pour une raison quelconque l’utilisateur peut accéder à la page du formulaire sans passer par le widget, il se peut que vous ayez envie de cacher l’en-tête. Sinon on verra une mention « Session du   au », ce qui pourra paraître bizarre.

Dans ce cas, on peut tout à fait ne laisser que {querystring:date_de_debut}, {querystring:date_de_fin} {querystring:ville}. dans le champ « valeur par défaut » de ninja form et ajouter ces informations dans le html directement

&ville= à ‘. $city .’&date_de_debut= Session du’ . $event_start_date .’&date_de_fin= au ‘ . $event_end_date

Tester !!

Maintenant, au clic sur une des dates de mon widget, je suis redirigé vers mon formulaire, c’est la partie « http://localhost/monsite/inscription/ » de mon lien. Si vous avez bien fait les choses, vous verrez dans l’url vos query après le « ? » et les valeurs correspondantes à chaque query string.

N’hésitez pas, si vous avez des idées d’amélioration ou des corrections à apporter à ce tuto !