'>

API des formulaires Drupal

Cette documentation regroupe des articles sur l'API des formulaires de Drupal afin d'en faciliter la prise en main.

Si vous êtes habitués au PHP procédural, comprendre comment fonctionne Drupal pour générer les formulaires et traiter leurs données peut être déstabilisant. Voici les deux principales choses dont vous ne devez plus vous préoccupez en utilisant l'API des formulaires de Drupal :
  • rédiger le html de votre formulaire : Drupal génère lui même de A à Z le html d'un formulaire. Vous devez simplement lui précisez des informations des champs que vous désirez afficher (type, titre, valeur par défaut etc...) via un array().
  • récupérer les données en utilisant $_POST, $_GET etc... : L'API des formulaires récupère les données et les rend accessibles dans la variable $form_state['values'] que vous verrez plus bas.
Gérer un formulaire dans Drupal va en fait se constituer en 4 grandes étapes :
1) Créer une fonction qui va définir les éléments que va contenir votre formulaire
2) Ecrire une fonction de validation des données (permettant par exemple de vérifier que le champ "numéro de téléphone" contient bien uniquement des chiffres)
3) Ecrire une fonction de soumission du formulaire, qui va nous permettre de faire quelque chose des données transmises par le formulaire : les enregistrer dans la base de données par exemple.
4) Afficher le formulaire ! La fonction drupal_get_form() va permettre de générer le html de notre formulaire et de l'afficher.
Voici un exemple de code d'un formulaire très simple. Pour cela j'ai crée un petit module afin de pouvoir afficher ce formulaire sur une page dédiée. Vous trouverez ce module d'exemple attaché à cette page en bas.
n'oubliez pas de vider le cache de Drupal pour que votre nouveau item de menu soit pris en compte ! Sinon votre page ne s'affichera pas.
<?php/<strong>
*
Implémentation de hook_menu. Déclencher l'appel de la fonction (callback) 'exemple_de_page'
* Lorsqu'
on se rend sur l'url 'exemple-de-page'
*/
function formexample_menu(){
  $items = array();
  $items['
exemple-de-page'] = array(
    '
title' => 'Exemple de formulaire', // titre de la page quand on se rendra sur l'url 'exemple-de-page'
    'page callback'
=> 'exemple_de_page', // La fonction appelée lorsqu'on se rend sur l'url exemple-de-page
   
'access arguments' => array('access content'), //un accès doit obligatoirement être précisé
   
'type' => MENU_CALLBACK, //indique que cet item de menu n'apparaitra dans aucun menu de Drupal, il est invisible.
   
);
  return
$items;
} /</strong>
*
callback pour notre item de menu défini ci-dessus*/
function
exemple_de_page(){
 
$output = 'Hello world ! ';
 
$output.= drupal_get_form('mon_formulaire');
  return
$output;
}

/**
* Création de notre formulaire en utilisant l'API de Drupal
*/
function mon_formulaire(){
 
$form = array();
 
$form['nom'] = array(
   
'#type' => 'textfield',
   
'#title' => t('Votre nom'), 
   
'#required' => TRUE,
  );
 
$form['submit'] = array(
   
'#type' => 'submit',
   
'#value' => t('OK'),  
  );
  return
$form;
}

/<strong>
*
Fonction de validation du formulaire* Il suffit d'ajouter _validate à la fin du nom de la fonction mon_formulaire ci-dessus pour que drupal
* trouve cette fonction automatiquement
*/
function mon_formulaire_validate($form, &$form_state){
  if($form_state['
values']['nom'] == 'John'){
    return form_set_error('
nom', "Désolé, John, vous n'êtes pas autorisé à vous inscrire sur le site");
  }
}

/</strong>
* Fonction de soumission du formulaire : on peut ici insérer les données du formulaire dans la base de données par exemple.
* La encore, la convention de nommage avec _submit permet à drupal
* de retrouver automatiquement la fonction de soumission
*/
function mon_formulaire_submit($form, &$form_state){
  drupal_set_message(
$form_state['values']['nom']);
 
$form_state['redirect'] = 'node'; // on redirige l'utilisateur sur la page de notre choix !
}
?>
 
Nous avons vu précédemment que le html de notre formulaire est généré automatiquement par l'API des formulaires de Drupal. Mais comment faire si nous voulons justement controler la mise en forme html de nos éléments, le présenter par exemple en trois colonnes, chacune étant un div avec un titre ? La réponse est : en utilisant une fonction theme.
Ecrire une fonction de theme
Pour cela nous devons déclarer une nouvelle fonction chargé de mettre en forme notre formulaire. Dans notre exemple, la fonction générant notre formulaire s'appelle "mon_formulaire". Si nous créons une fonction nommée "theme_mon_formulaire", celle ci sera utilisée par le systeme de theme de Drupal pour mettre en forme notre formulaire d'exemple à la place de l'affichage par défaut.
<?phpfunction theme_mon_formulaire($form){
 
$output  = drupal_render($form['submit']);
 
$output .= '<div>';
 
$output .= '<h1>Entrez votre nom ! </h1>';
 
$output .= drupal_render($form['nom']);
 
$output .= '</div>'
 
$output .= drupal_render($form);
  return
$output;
}
?>
Explications :
- La fonction drupal_render() est utilisée pour généré le html d'une partie de notre formulaire. drupal_render($form['submit']) va générer automatiquement le code html pour notre bouton de soumission.
- Notez bien que à la fin nous faisons un drupal_render sur tout le formulaire afin d'être sur qu'aucun élément de notre formulaire n'a été oublié par notre fonction theme : on est ainsi sûr d'afficher tout ce que nous n'avons pas affiché manuellement par drupal_render() et également que les champs cachés dont Drupal a besoin pour traiter un formulaire seront eux aussi bien présents.
Déclarer à Drupal notre fonction theme !
Le code ci-dessus ne modifiera en aucune façon l'affichage de votre formulaire pour le moment. Drupal doit en effet savoir que cette fonction theme existe, la convention de nommage avec 'theme_+nom_du_formulaire' n'est pas suffisante sur Drupal 6. Drupal fonctionne avec un "registre de themes" (theme registry) : il s'agit de la liste de toutes les fonctions themes déclarées par les modules.
Il faut donc maintenant que nous informions Drupal de l'existence de notre fonction theme, et pour cela nous devons la déclarer au registre des themes via un hook dans notre module :
<?phpfunction formexample_theme(){
 
$themes = array();
 
$themes['mon_formulaire'] = array(
   
'arguments' => array(), //notre fonction n'a besoin d'aucun argument, on laisse vide.
 
);
  return
$themes;
}
?>
Un hook_theme peut contenir plusieurs themes, c'est pour cela qu'il est déclaré en tant que array.
Ici on indique l'existence d'un theme 'mon_formulaire', ce qui signifie que Drupal cherchera la fonction "theme_mon_formulaire" pour générer notre html.
Il faut enfin vider le cache de Drupal (site > configuration > performance > vider le cache) afin que drupal rescanne tous les modules pour reconstruire le registre de thèùes en prenant en compte notre nouvelle fonction.
Déclarer une fonction theme alternative pour un formulaire
Il existe une autre manière de déclarer quel fonction theme doit utiliser le formulaire : en la déclarant dans la fonction 'mon_formulaire' en ajoutant ceci :
<?php
$form
['#theme'] = 'mon_formulaire_alternatif';?>
il faudra ensuit également avertir le theme de registrer de l'existence de cette nouvelle fonction theme via notre hook theme qui ressemblera alors à ceci :
<?phpfunction formexample_theme(){
 
$themes = array();
 
$themes['mon_formulaire'] = array(
   
'arguments' => array(),
  );
 
$themes['mon_formulaire_alternatif'] = array(
   
'arguments' => array(),
  );
  return
$themes;
}
?>
Après avoir vidé le cache, drupal cherchera la fonction theme_mon_formulaire_alternatif. Celle ci sera cherchée avant la fonction theme_mon_formulaire qui du coup ne sera pas utilisée, si ces deux fonctions themes co-existent.

Le code ci-dessous est simplement un exemple concret de module pour altérer un formulaire et le themer de façon plus poussé que dans les exemples précédents.
Ici le module s'appelle "formulaire_tableau" : le but est simplement d'améliorer la présentation d'un formulaire produit (node/add/tableaux dans ce cas) pour la boutique en ligne ubercart : le formulaire de node étant étant déjà altéré par ubercart, taxonomie, CCK, le résultat final niveau présentation est catastrophique : ici je fais une restructuration complète du formulaire afin d'en controler totalement la présentation et de réordonner les champs de façon logique pour l'utilisateur final; et également pour remplir de façon automatique le champ référence.
<?phpfunction formulaire_tableau_form_alter(&$form, $form_state, $form_id){
  global
$user;
 
// dans le cas ou on ajoute un produit de type tableau
 
if($form_id == 'tableaux_node_form'){
   
// dire d'utiliser la fonction "theme_formulaire_tableau" quand le formulaire sera transformé en html
   
$form['#theme'] = 'formulaire_tableau';  
   
// créer automatiquement une nouvelle référence ubercart à la création d'un produit
   
arg(1) == 'add' ? $form['base']['model']['#value'] = nouvelle_reference_tableau() : '';
   
// Masquer ce champ sauf pour le super admin
   
$user->uid != 1 ? form['base']['model']['#type'] = 'hidden' : '';    
   
// j'enleve des descriptions qui genent ma présentation
   
$form['title']['#description'] = '';
   
$form['taxonomy']['tags'][2]['#description'] = '';
   
$form['taxonomy']['tags'][3]['#description'] = '';
   
$form['taxonomy']['tags'][4]['#description'] = '';
   
$form['taxonomy']['tags'][5]['#description'] = '';
   
$form['taxonomy']['tags'][6]['#description'] = '';
   
$form['base']['prices']['sell_price']['#description'] = '';
   
$form['buttons']['#weight'] = 100; // s'assurer que les boutons de soumission restent bien en bas
 
}
} // cette fonction créer une référence automatique pour un nouveau tableaufunction nouvelle_reference_tableau(){
 
// trouver la dernière référence existante, puis ajouter 1
 
return db_result(db_query_range("SELECT model FROM {uc_products} ORDER BY nid DESC", 0, 1)) + 1;
}

// redonner un visgage humain au formulaire node/add/tableaux et ordonner les champs à notre sauce
// (taxonomie, ubercart, cck)
function theme_formulaire_tableau($form){
 
drupal_set_title("Ajouter un tableau"); 
 
$html .= '<p>Ajouter un nouveau tableau dans votre boutique.</p>';
 
$html .= drupal_render($form['title']);
 
$html .= drupal_render($form['taxonomy']['tags'][5]);
 
$html .= drupal_render($form['base']['dimensions']['width']); 
 
$html .= drupal_render($form['base']['dimensions']['height']); 
 
$html .= drupal_render($form['field_heightc']); 
 
$html .= drupal_render($form['field_widthc']);
 
$html .= drupal_render($form['field_localisation']);
 
$html .= drupal_render($form['taxonomy']['tags'][4]);
 
$html .= drupal_render($form['taxonomy']['tags'][3]);
 
$html .= drupal_render($form['taxonomy']['tags'][6]);
 
$html .= drupal_render($form['base']['prices']['sell_price']);
 
$html .= drupal_render($form['field_source']);
 
$html .= drupal_render($form['taxonomy']['tags'][2]);  
 
$html .= drupal_render($form['field_disponibilite']); 
 
$html .= drupal_render($form['field_provenance']);  
 
$html .= drupal_render($form['field_image_cache']);
 
$html .= drupal_render($form['base']['model']);
 
$html .= drupal_render($form['base']['dimensions']['lenght']);
 
$html .= '<div style = "display :none">';
 
$html .= drupal_render($form['base']);
 
$html .= '</div>';
 
// J'Ajoute tous les champs qui n'ont pas été énoncé ci dessus !
 
$html .= drupal_render($form);
  return
$html;
}

// TRES IMPORTANT : on déclare à drupal notre theme. Sans ce hook,
// le theming ne fonctionnera pas.
function formulaire_tableau_theme(){
 
$themes = array();
 
$themes['formulaire_tableau'] = array(
   
'arguments' => array(), //notre fonction n'a besoin d'aucun argument, on laisse vide.
 
);
  return
$themes;
}
?>