Comment un formulaire de contact unique peut-il atterrir dans la boîte mail du bon artisan, sans installer un plugin de routage complet pour six corps de métier différents ? C’est la question posée par un collectif d’artisans qui partagent un site vitrine commun : menuisier, électricien, plombier, carreleur, peintre et couvreur figurent tous sur la même page « Contact », avec un unique champ de sélection.
La réponse tient en une trentaine de lignes de code, à condition de s’appuyer sur les bons points d’entrée de WordPress plutôt que d’installer une usine à gaz pensée pour des formulaires à embranchements complexes. Voici le problème, le code qui le résout, puis les variantes utiles selon le contexte.
Le problème posé
Le formulaire de contact propose un champ metier (menuiserie, électricité, plomberie, etc.) et doit envoyer le message à l’adresse de l’artisan correspondant, avec une copie systématique à une adresse de secours en cas d’absence prolongée. Un plugin de formulaires généraliste sait faire du routage conditionnel, mais impose souvent une interface de règles disproportionnée pour six cas fixes et connus à l’avance.
Le snippet commenté

La solution repose sur trois éléments standards de WordPress : un formulaire HTML classique posté vers admin-post.php, une action accrochée en admin_post_nopriv_ pour les visiteurs non connectés, et une table de correspondance stockée en option, modifiable sans toucher au code :
// Table de correspondance, modifiable depuis l’administration
function artisans_table_routage() {
return get_option( 'artisans_routage', array(
'menuiserie' => 'menuiserie@collectif-artisans.test',
'electricite' => 'electricite@collectif-artisans.test',
'plomberie' => 'plomberie@collectif-artisans.test',
'carrelage' => 'carrelage@collectif-artisans.test',
'peinture' => 'peinture@collectif-artisans.test',
'couverture' => 'couverture@collectif-artisans.test',
) );
}
add_action( 'admin_post_nopriv_contact_artisan', 'artisans_traiter_contact' );
add_action( 'admin_post_contact_artisan', 'artisans_traiter_contact' );
function artisans_traiter_contact() {
if ( ! isset( $_POST['artisans_nonce'] ) || ! wp_verify_nonce( $_POST['artisans_nonce'], 'contact_artisan' ) ) {
wp_die( 'Requête invalide.' );
}
$metier = sanitize_key( $_POST['metier'] ?? '' );
$message = sanitize_textarea_field( $_POST['message'] ?? '' );
$email_de = sanitize_email( $_POST['email'] ?? '' );
$table = artisans_table_routage();
$vers = $table[ $metier ] ?? 'contact@collectif-artisans.test';
wp_mail(
$vers,
'Nouvelle demande de contact',
$message . "\n\nRépondre directement à : " . $email_de,
array( 'Cc: secours@collectif-artisans.test' )
);
wp_safe_redirect( home_url( '/merci' ) );
exit;
}
Le champ metier transmis par le formulaire est validé avec sanitize_key(), ce qui élimine tout caractère indésirable avant de servir de clé au tableau de correspondance : une valeur absente de la table retombe automatiquement sur l’adresse générale, sans erreur PHP.
Pourquoi admin-post.php plutôt qu’un endpoint REST
Pour un formulaire de contact classique, sans besoin d’interaction JavaScript asynchrone, admin-post.php reste le point d’entrée le plus simple : il gère nativement les utilisateurs connectés et non connectés via les deux préfixes d’action, sans avoir à déclarer de route REST ni à gérer de nonce d’API distinct. C’est un choix pragmatique, pas un choix par défaut : dès qu’un enrichissement JavaScript devient nécessaire (validation en temps réel, envoi sans rechargement), un contrôleur REST personnalisé reprend tout son sens.
Variantes utiles
Router selon plusieurs critères combinés
Si le collectif ajoute une notion de zone géographique en plus du métier, la table de correspondance devient un tableau à deux niveaux, indexé par metier puis par zone, avec un repli sur le métier seul si la zone n’est pas renseignée. Le principe de repli progressif reste identique : toujours prévoir une valeur par défaut plutôt qu’un email non trouvé.
Journaliser les demandes sans base de données dédiée
Pour garder une trace des demandes envoyées sans créer de table personnalisée, chaque envoi peut être enregistré comme un post du type demande_contact avec le métier en métadonnée, consultable depuis l’administration standard sans développement d’écran supplémentaire.
Alerter en cas d’échec d’envoi
Comme wp_mail() ne renvoie qu’un booléen, il est utile d’accrocher le hook wp_mail_failed, qui reçoit un objet WP_Error, pour journaliser précisément la cause d’un échec d’envoi plutôt que de constater après coup qu’un artisan n’a jamais reçu ses demandes.
En résumé
Pour un nombre de destinataires fixe et connu à l’avance, une table de correspondance en option et un traitement via admin-post.php couvrent le besoin sans complexité superflue. Le jour où le nombre de métiers ou de critères de routage explose, la bascule vers un contrôleur REST personnalisé ou un plugin de formulaires dédié redevient justifiée, mais elle n’est pas nécessaire pour six corps de métier stables.