La classe
FormHelper permet d'écrire facilement des formulaire HTML.
Son utilisation n'est pas obligatoire, mais elle est recommandée.
Configuration
La classe dispose de quelques options de configuration statiques s'appliquant à toutes les utilisations ultérieures de la classe.
- FormHelper::withJQueryUI(TRUE) (par défaut)
Lors de la création d'un input de modification de date, on va appeler JQueryUI pour le transformer en datepicker.
Si votre application PHP l'utilise pas JQuery UI ou que ce comportement n'est pas souhaité, vous pouvez le désactiver en appelant l'API avec FALSE
- FormHelper::useImprovedCheckbox(TRUE) (par défaut)
Lors de la création de checkbox, permet de générer un code HTML qui va envoyer "0" comme valeur si la case n'est pas cochée et "1" si elle est cochée.
Vous pouvez désactiver ce comportement en appelant l'API avec FALSE : cela restaurera le comportement de base des checkbox : Le nom ne sera pas envoyé du tout si la case n'est pas cochée, et la valeur sera "on" si elle est cochée.
Types
Il existe 2 types de formulaire : GET et POST
- GET: les paramètres du formulaire seront envoyées dans l'URL.
Ce format devrait être utilisé pour tout ce qui est consultation
Les formulaires en GET produisent des URL qui sont indexables par les moteurs de recherche, même avec une URL complexe de
la forme ?search[author][name]=Douglas&search[book]=guide&search[note]=5, la page pourra être indexée
et son résultat sera cohérent et logique avec l'URL fournie.
- POST: les paramètres du formulaire seront envoyées en POST et non dans l'URL.
Ce format devrait être utilisé pour tout ce qui est modification
Les formulaires en POST ne sont pas indexés par les moteurs de recherche. Les utiliser pour réaliser des modifications
assurera qu'un moteur ne modifiera pas "par erreur" une page du site. Si on imagine un forum où l'ajout d'un message
soit réalisé avec un formulaire en GET, l'indexation d'une URL d'ajout pourrait vite tourner à la catastrophe.
Déclaration
Pour écrire un formulaire, on peux utiliser l'une des syntaxes suivantes :
<?php
echo FormHelper::get();
// Contenu du formulaire
echo FormHelper::end();
// ou encore :
echo FormHelper::post();
// Contenu du formulaire
echo FormHelper::end();
Par défaut les formulaires renvoient sur la page courante, mais il est possible d'indiquer une autre page dans le 1er paramètre.
<?php
echo FormHelper::get(WEB_RELATIVE.'autre/chemin/page.php');
// Contenu du formulaire
echo FormHelper::end();
// Va générer :
// <form method="get" action="../autre/chemin/page.php">
// </form>
Si la page utilisée comme action (URL courante si le 1er paramètre est NULL, ou 1er paramètre sinon) a des paramètres dans
son URL, il faut indiquer dans le 2ème paramètre lesquels on souhaite conserver et/ou surcharger. Par défault les paramètres
sont
ignorés.
<?php
echo FormHelper::post(WEB_RELATIVE.'autre/chemin/page.php?a=1&b=2&c=3', array('a', 'b' => 4, 'd' => 5, 'e'));
// Contenu du formulaire
echo FormHelper::end();
// Va générer :
// <form method="post" action="../autre/chemin/page.php?a=1&b=4&d=5"> // a => 1, b => 4, d => 5
// </form>
Dans l'exemple précédent on voit que dans le tableau passé en 2ème paramètre :
- Si le nom d'une variable est renseignée sans valeur associée (a), elle est recopiée avec sa valeur de l'URL d'origine, sauf si
elle n'existe pas (e)
- Si le nom d'une variable est renseignée avec une valeur associée, elle remplace une valeur existante (b) ou est ajoutée si
elle n'existe pas (d)
On peut également indiquer la valeur spéciale
"*" pour dire de recopier tout les paramètres de la requête ainsi que
clé=>NULL pour supprimer un paramètre de la requête
Les paramètres sont interprétés dans l'ordre de leur déclaration, celui-ci a donc une importance :
<?php
// avec une page ?a=1&b=2&c=3
echo FormHelper::post(NULL, array('*', 'a' => NULL); // va recopier tout les paramètres, puis supprimer 'a' : ?b=2&c=3
echo FormHelper::post(NULL, array('a' => NULL, '*'); // va supprimer 'a' puis recopier tout les paramètres : ?a=1&b=2&c=3
Dans le cas où le formulaire est de type GET, le code généré sera un peu différent. En effet, la spécification HTML indique
que les paramètres d'une requête sont ignorés en cas de GET. Pour les conserver le framework va donc générer des balises
hidden, dans une balise <p> afin de rester compatible XHTML, juste après la balise <form> :
<?php
echo FormHelper::get(WEB_RELATIVE.'autre/chemin/page.php?a=1&b=2&c=3', array('a', 'b' => 4, 'd' => 5, 'e'));
// Contenu du formulaire
echo FormHelper::end();
// Va générer :
// <form method="get" action="../autre/chemin/page.php">
// <p style="display:none">
// <input name="a" value="1" type="hidden">
// <input name="b" value="4" type="hidden">
// <input name="d" value="5" type="hidden">
// </p>
// </form>
Enfin, le 3ème paramètre permet d'ajouter n'importe quel autre attribut sur la balise form générée :
<?php
echo FormHelper::get(NULL, NULL, array('data-name' => '"Fladnag"', 'target' => '_blank'));
// Contenu du formulaire
echo FormHelper::end();
// Va générer :
// <form data-name=""Fladnag"" target="_blank" method="get" action="/manual/tutorial/display/formhelper/">
// </form>
Pour ajouter un champ au formulaire, on a deux possibilités :
Pour afficher un formulaire de modification d'un champ d'un objet mappé, il suffit d'écrire
<?php
ViewControl::edit(); // Important ! Sans cela le champ de formulaire ne s'affichera pas
echo FormHelper::post();
echo $monObjet->FORM->monChamp;
echo FormHelper::end();
La balise affichée dépend avant tout du type du champ, comme décrit dans
la section précédente
La manière dont la balise est générée ensuite dépend de nombreux facteurs.
Pour écrire une balise, on a besoin des informations suivantes :
- type (string): type de balise à générer parmis les types supportés :
text, password, hidden, select, checkbox,
textarea, radio
- name (string): valeur de l'attribut
name. Permettra de retrouver la valeur du champ après l'envoi du formulaire
- value (string): valeur courante de la balise
- format (string): pour les dates uniquement, format d'affichage de celle-ci
- classes (string): liste des classes CSS à utiliser dans l'attribut "class"
- options (array): si le tag généré est de type SELECT ou RADIO uniquement: liste d'options à utiliser sous la forme (valeur => Texte à afficher)
- autres (array): liste des autres attributs sous la forme clé=>valeur
Ces informations peuvent provenir de différentes sources, dans l'ordre de priorité croissante :
- $_GET / $_POST : La valeur provenant de ces tableaux est toujours prioritaire pour l'information value
- Paramètres de FORM : Format simple (string) ou tableau contenant plusieurs informations au format clé=>valeur
- Métadonnées d'affichage du champ : avec displayOptions()
on peut spécifier un tableau d'informations par défaut pour afficher le formulaire. Cela concerne toutes les informations listées
au-dessus, y compris
type
- Métadonnées du champ Les différents paramètres de construction d'un champ sont réutilisés lors de l'affichage.
Le tableau ci-dessous liste, par ordre de priorité décroissante, ce qui peut être spécifié et ce qui est réellement utilisé pour construire
un tag HTML de formulaire:
| Source | Type | $format | name | class | options | value | Autres attributs |
| $_GET / $_POST | | | | | | X | |
| Paramètre de méthode FORM | type | format | name | class | options | value | Autres clés |
| Meta:displayOptions() | type | format | name | class | options | value | Autres clés |
| Metadonnées | type/$nullable | $displayFormat | $name | | $values/type/$nullable | | |
Les éléments utilisés de la source
Metadonnées en détail :
| Type de champ | Type | options | $format |
| Number/Text sans liste de valeurs | text | | |
| Number/Text non null avec liste de valeurs | select | La liste indiquée | |
| Number/Text nullable avec liste de valeurs | select | L'option ''=>'' + la liste indiquée | |
| Date | text | | $displayFormat |
| Boolean non null | checkbox | | |
| Boolean nullable | select | ''=>'', 1=>'Oui', 0=>'Non' | |
Les exemples ci-dessous montrent les différents cas d'utilisations :
<?php
class MonObjet extends Base {
public function metadata() {
parent::MODEL()->registerFields(
Field::newNumber('status', 'Status', TRUE, 1, array(
1=>'Nouveau',
2=>'En cours',
3=>'Terminé',
))->displayOptions(array(
'class' => 'c1 c2',
'format' => 'aaa',
)),
Field::newBoolean('visible', 'Visible', FALSE, TRUE), // non NULL, TRUE par defaut
Field::newBoolean('visible2', 'Visible2', TRUE, TRUE), // NULLABLE, TRUE par defaut
Field::newBoolean('decision', 'Décision', TRUE, NULL)->displayOptions(array( // NULLABLE, NULL par defaut
'options'=>array(
1 => 'A corriger',
0 => 'A ne pas corriger',
)
)),
Field::newDate('date', 'Date', SqlDateFormat::DATETIME),
);
}
}
$obj = new MonObjet();
$obj->date = time();
ViewControl::edit();
echo FormHelper::get();
echo 'Status : '.$obj->FORM->status.'<br/>'; // Affiche :
// <select class="c1 c2" name="status"> // c1 c2 proviennent du displayOptions
// <option value=""> </option> // Cette option est générée car le champ est NULLABLE
// <option selected="selected" value="1">Nouveau</option> // La valeur par défaut du champ est selectionnée
// <option value="2">En cours</option>
// <option value="3">Terminé</option>
// </select>
echo 'Status C3 : '.$obj->FORM(array('class' => 'c3'))->status.'<br/>'; // affiche le meme select, mais c1 c2 sont remplacés par c3
echo 'Visible : '.$obj->FORM->visible.'<br/>'; // affiche :
// <input type="checkbox" checked="checked" name="visible"> // Un booléen non null est représenté par une case à cocher
echo 'Visible 2 : '.$obj->FORM->visible2.'<br/>'; // affiche :
// <select name="visible2"> // Un booléen NULLABLE est représenté par une liste à 3 états
// <option value=""> </option>
// <option selected="selected" value="1">Oui</option> // par défaut, les textes générés sont Oui/Non
// <option value="0">Non</option>
// </select>
echo 'Decision : '.$obj->FORM->decision.'<br/>'; // affiche :
// <select name="decision">
// <option selected="selected" value=""> </option>
// <option value="1">A corriger</option> // ici les textes générés sont surchargés par displayOptions
// <option value="0">A ne pas corriger</option>
// </select>
echo 'Date : '.$obj->FORM->date.'<br/>'; // affiche :
// <input type="text" value="dd/mm/yyyy" name="date"> // avec dd/mm/yyyy de la forme d/m/Y qui est le format par défaut d'affichage des dates
echo 'Date : '.$obj->FORM('Y')->date.'<br/>'; // affiche :
// <input type="text" value="yyyy" name="date"> // avec yyyy l'année, le format surchargé étant Y
echo 'Date : '.$obj->FORM(array(
'format' => 'Y/m/d',
'title' => 'the date',
'name' => 'date3',
))->date.'<br/>'; // affiche :
// <input type="text" value="yyyy/mm/dd" name="date3" title="the date">
echo 'Date : '.$obj->FORM(array(
'type' => 'select', // ici on change completement le type du tag généré
'options' => array('a'=>'01/01/1970', 'b'=>'26/10/1985'), // et on peut spécifier les options souhaitées
'name' => 'date4',
))->date.'<br/>'; // affiche :
// <select name="date4">
// <option value="a">01/01/1970</option>
// <option value="b">26/10/1985</option>
// </select>
echo FormHelper::end();
On peut utiliser directement les méthodes statiques de la classe
FormHelper pour générer des tag HTML de formulaire
Il y a 4 méthodes :
- FormHelper::input($name, $type = 'text', $value = NULL, $classes = array(), array $others = array())
Permet de générer un tag <input .../>
- FormHelper::textarea($name, $value = NULL, $classes = array(), array $others = array())
Permet de générer un tag <textarea ...>...</textarea>
- FormHelper::select($name, array $options, $value = NULL, $classes = array(), array $others = array())
Permet de générer un tag <select ...><option ...>...</select>
- FormHelper::radio($name, array $options, $value = NULL, $classes = array(), array $others = array())
Permet de générer un tag <input type="radio" .../> ...
Les différents paramètres ne doivent pas être échappés pour l'affichage HTML, ils le seront par ces méthodes avant leur utilisation,
y compris pour le tableau $options
Le paramètre $value, si fourni, sera prioritaire par rapport à toutes les autres sources. En clair cela veux dire que le champ aura
toujours la valeur de $value. Si on souhaite que le champ puisse prendre pour valeur la précédente valeur soumise, on laissera
$value à NULL et on pourra éventuellement renseigner la clé 'value' dans $others pour indiquer la valeur du champ si aucune autre
source n'est disponible.
Les attributs NULL ne seront pas ajoutés, et les options dont le texte est une chaine vide seront remplacées par
(espace insécable)
<?php
ViewControl::edit(); // Important ! Sans cela le champ de formulaire ne s'affichera pas
echo FormHelper::post();
echo FormHelper::select('simpleSelect', array('A'=>'Arbre', 'B'=>'Bizarre', 'C'=>'Chouette')); // affiche une liste déroulante
// avec les options Arbre, Bizarre et Chouette
echo FormHelper::input(NULL, 'submit', 'Envoyer'); // affiche un bouton submit simple
echo FormHelper::end();
Gestion des noms
Il est possible de définir un préfixe pour les noms à utiliser dans les formulaires.
Par exemple, si on fait un formulaire de saisie ou modification multiple contenant plusieurs lignes du même type d'objet, on va générer
une liste de balises qui devront avoir des noms différents, par exemple de la forme
saisie[id_ligne][name_field]
Pour faire cela, il suffit d'utiliser
FormHelper::withNameContainer(...) et
FormHelper::withoutNameContainer
pour arrêter d'utiliser un préfixe.
La méthode
withNameContainer prend un nombre indéfini de paramètres : chacun paramètre sera ajouté comme préfixe. Il sera donc
possible d'avoir un préfixe contenant plusieurs ids, par exemple
book[id][chapter_id][paragraphe_id][...]
<?php
$listeObjet = array();
$listeObjet[0] = new MonObjet();
$listeObjet[1] = new MonObjet();
$listeObjet[0]->id = 1;
$listeObjet[1]->id = 2;
ViewControl::edit();
echo FormHelper::post();
foreach($listeObjet as $obj) {
FormHelper::withNameContainer('saisie', $obj->id); // tout les noms vont être de la forme saisie[id_object][...]
echo 'Champ1 : '.$obj->FORM->champ1;
echo 'Champ2 : '.$obj->FORM->champ2;
FormHelper::withoutNameContainer();
echo '<br/>';
}
echo FormHelper::end();
// Va générer :
// Champ1 : <input type="text" value="" name="saisie[1][champ1]"/>
// Champ2 : <input type="text" value="" name="saisie[1][champ2]"/>
// <br>
// Champ1 : <input type="text" value="" name="saisie[2][champ1]"/>
// Champ2 : <input type="text" value="" name="saisie[2][champ2]"/>
Ajout de code javascript
Il est possible d'ajouter des blocs de code javascript lors de la construction d'un formulaire.
Les codes ajoutés seront générés à la fin du formulaire, lors de l'utilisation de
FormHelper::end()
Exemple :
<?php
ViewControl::edit();
echo FormHelper::get();
FormHelper::registerJavascript('key', 'alert(1)');
echo FormHelper::end();
On peut également :
- Exécuter du code lorsque la page est chargée avec registerJSPageLoaded()
- Remplacer des valeurs avec registerJSTokenValue()
<?php
ViewControl::edit();
echo FormHelper::get();
// enregistre une nouvelle fonction javascript
FormHelper::registerJavascript('log', <<<JS
function log(e) {
console.log(e);
}
JS
);
// récupération de la clé JS de chargement de page
$keyPageLoaded = FormHelper::registerJSPageLoaded();
// on ajoute des instructions a exécuter au chargement de la page
FormHelper::registerJSTokenValue($keyPageLoaded, 'log("begin")');
// on ajoute une instruction avec la clé "keyValue"
FormHelper::registerJSTokenValue($keyPageLoaded, 'log("bad value")', 'keyValue');
// on remplace l'instruction avec la clé "keyValue"
FormHelper::registerJSTokenValue($keyPageLoaded, 'log("good value")', 'keyValue');
// on ajoute un morceau de code avec un nouveau token
FormHelper::registerJSTokenValue($keyPageLoaded, 'log("{VALUES}")');
// on ajoute des valeurs au nouveau token
FormHelper::registerJSTokenValue('VALUES', '1', NULL, ", ");
FormHelper::registerJSTokenValue('VALUES', '2', NULL, ", ");
FormHelper::registerJSTokenValue('VALUES', '3', NULL, ", ");
FormHelper::registerJSTokenValue($keyPageLoaded, 'log("end")');
echo FormHelper::end();
?>
Va produire le code javascript suivant :
function log(e) {
console.log(e);
}
if (typeof(jQuery)!="undefined") {
jQuery(function() {
log("begin")
log("good value")
log("1, 2, 3")
log("end")
});
} else {
document.addEventListener("DOMContentLoaded", function(event) {
log("begin")
log("good value")
log("1, 2, 3")
log("end")
});
}