OVH Cloud OVH Cloud

Le debat du mois : gestion des formulaires

18 réponses
Avatar
John Gallet
Bonjour/bonsoir,

Du temps de fciwap, on faisait parfois un mini débat sur un thème classique
et si des choses intéressantes en ressortaient, je faisais un résumé pour la
FAQ. Je relance l'idée, on verra bien si ça intéresse les foules.

Le sujet du moment : vous devez gérer un formulaire avec deux variables
obligatoires, une facultative. Si les deux obligatoires sont reçues, vous
faites un insert en sgbdr. A noter que l'une des variables est clef primaire
de la table histoire de compliquer un peu. Sinon vous regénérez le
formulaire en préremplissant les champs déjà saisis. Comment faites vous ça
proprement (avec séparation code / présentation maximale, sans pour autant
utiliser des templates, avec gestion d'erreurs élégantes pour l'internaute
etc...).

a++
JG

10 réponses

1 2
Avatar
Harry Cover
On 25 Feb 2004 00:52:46 GMT, John Gallet
wrote:

Bonjour/bonsoir,

Du temps de fciwap, on faisait parfois un mini débat sur un thème classique
et si des choses intéressantes en ressortaient, je faisais un résumé pour la
FAQ. Je relance l'idée, on verra bien si ça intéresse les foules.
Bonne idée. C'est un peu de la créa de groupe, c'est trop rarement

fait ;)

Le sujet du moment : vous devez gérer un formulaire avec deux variables
obligatoires, une facultative. Si les deux obligatoires sont reçues, vous
faites un insert en sgbdr. A noter que l'une des variables est clef primaire
de la table histoire de compliquer un peu. Sinon vous regénérez le
formulaire en préremplissant les champs déjà saisis. Comment faites vous ça
proprement (avec séparation code / présentation maximale, sans pour autant
utiliser des templates, avec gestion d'erreurs élégantes pour l'internaute
etc...).
Euuh sans utiliser de templates, c'est vraiment bête... a moins de

faire du code vraiment petit. Il existe des classes vraiment toutes
petites pour gérer les templates de manière assez simple.

-Pour remplir la page --> une fonction qui prend 3 arguments
optionnels (les 3 champs de la page).
-Validation du formulaire --> une fonction qui controle les arguments
(anti SQL injection, etc...) fait la requête. Si problème, elle apelle
la première fonction en renseignant les paramètre. Ca fait de
l'économie de code.

Avatar
Etienne SOBOLE
"John Gallet" a écrit dans le message de
news:403bceb8$0$28122$

Le sujet du moment : vous devez gérer un formulaire avec deux variables
obligatoires, une facultative. Si les deux obligatoires sont reçues, vous
faites un insert en sgbdr. A noter que l'une des variables est clef
primaire

de la table histoire de compliquer un peu. Sinon vous regénérez le
formulaire en préremplissant les champs déjà saisis. Comment faites vous
ça

proprement (avec séparation code / présentation maximale, sans pour autant
utiliser des templates, avec gestion d'erreurs élégantes pour l'internaute
etc...).


C'est impossible de "faire ca proprement" si on utilise une chaine de
caractère comme clé primaire.
C'est pas compatible ;)

Disons plutot qu'il y a une règle d'unicité sur l'une des valeurs du
formaulaire dans la base.

Ca doit marcher avec quel SGBD ton truc?
Si on se limite a postgreSQL ca va?
bon ben j'essaye :)

Etienne

Avatar
John Gallet
Re,

Du temps de fciwap, on faisait parfois un mini débat sur un thème
classique


et si des choses intéressantes en ressortaient, je faisais un résumé pour
la


FAQ. Je relance l'idée, on verra bien si ça intéresse les foules.
Bonne idée. C'est un peu de la créa de groupe, c'est trop rarement

fait ;)


Ouais, je commence à préparer ma candidature de GO au Club' pour cet été.
"Allez les enfants et les papys, on va faire du PHp aujourd'hui" ;-))

Euuh sans utiliser de templates, c'est vraiment bête... a moins de
faire du code vraiment petit. Il existe des classes vraiment toutes
petites pour gérer les templates de manière assez simple.



Donne un exemple avec un template si tu veux, mais pour un pauvre formulaire
même à 30 champs, ça vaut pas le coup, enfin c'est mon avis et je le
partage. On va dire qu'on utilise ce que j'appelle "le template du pauvre"
(tm)(c)(jgallet 2001)

En revanche, tiens, comment garderais-tu un champ d'un SELECT à la valeur
déjà saisie en utilisant un template ? Ca ca peut être intéressant à
montrer.

-Pour remplir la page --> une fonction qui prend 3 arguments
optionnels (les 3 champs de la page).


Si tu fais une fonction, puisque deux sont obligatoires, tu devrais avoir 2
arguments obligatoires et éventuellement un par défaut (le facultatif).

-Validation du formulaire --> une fonction qui controle les arguments
(anti SQL injection, etc...)
Râââh lovely, enfin un qui y pense. Ca fait plaisir.


fait la requête. Si problème, elle apelle
la première fonction en renseignant les paramètre. Ca fait de
l'économie de code.


Ouaip. Perso, je le découperais pas pile poil comme ça mais dans les grandes
lignes je ferais pareil.

a++
JG


Avatar
John Gallet
Re,

C'est impossible de "faire ca proprement" si on utilise une chaine de
caractère comme clé primaire.

C'est pas compatible ;)



Mais si, mais si, on va dire que c'est un login par exemple. Mais si tu
préfères que ce soit un UNIQUE NOT NULL, ça fera pas beaucoup de
différences.

Disons plutot qu'il y a une règle d'unicité sur l'une des valeurs du
formaulaire dans la base.



Du premier champ, ça suffira.

Ca doit marcher avec quel SGBD ton truc?
Si on se limite a postgreSQL ca va?
bon ben j'essaye :)



Ce que tu veux :-) Si pour une fois on peut sortir de mysql, encore mysql et
toujours mysql, tant mieux.

John

Avatar
Patrick
Harry Cover wrote:
On 25 Feb 2004 00:52:46 GMT, John Gallet
wrote:

Le sujet du moment : vous devez gérer un formulaire avec deux variables
obligatoires, une facultative. Si les deux obligatoires sont reçues, vous
faites un insert en sgbdr. A noter que l'une des variables est clef primaire
de la table histoire de compliquer un peu. Sinon vous regénérez le
formulaire en préremplissant les champs déjà saisis. Comment faites vous ça
proprement (avec séparation code / présentation maximale, sans pour autant
utiliser des templates, avec gestion d'erreurs élégantes pour l'internaute
etc...).
Euuh sans utiliser de templates, c'est vraiment bête... a moins de

faire du code vraiment petit. Il existe des classes vraiment toutes
petites pour gérer les templates de manière assez simple.


Complètement d'accord, pour une séparation maximale entre code php et
code html, rien ne vaut les templates.

-Pour remplir la page --> une fonction qui prend 3 arguments
optionnels (les 3 champs de la page).
-Validation du formulaire --> une fonction qui controle les arguments
(anti SQL injection, etc...) fait la requête. Si problème, elle apelle
la première fonction en renseignant les paramètre. Ca fait de
l'économie de code.


Ha tiens, ben moi aussi je fais comme ça :)

Pour préciser

En haut de page, une constante qui contient l'url vers laquelle on se
dirige si la validation des variables du formulaire est OK (ça permet de
modifier le script rapidement)
define (NEXT_URL, '/monurl.php') ;

Une fonction display_form dont les paramètres ont la valeur vide par
défaut.

function display_form($msg='', $var1='', $var2='', $var3='') {
//ici code pour afficher le formulaire
// la variable $msg contient un éventuel message d'erreur à afficher
}

Une fonction validate, sans paramètres, j'utiliserais le super_global
$_POST[] pour accéder aux variables POST (idem GET)

function validate() {
//ici code de validation (j'ai créé une bibliothèque de fonctions
//classiques de validation, genre chaîne vide, test de longueur
//(maxi/mini), email valide, ...
// Attention, si l'un des champs est une clé primaire, il faut faire
// une requête pour vérifier l'existence éventuelle d'un record avec
// cette clé !
// Si invalidite $msg prend une chaîne de caractère expliquant
// pourquoi il y a invalidité

// if valide
header('Location: '.NEXT_URL) ;
// else
display_form($msg, $_POST['var1'], $_POST['var2'], $_POST['var3']) ;
}

Vient ensuite un code d'aiguillage en fonction d'un paramètre $act
initialisé

if (!isset($act) or ($act == '')) { $act = 'display' ; }

switch ($act) {
case 'display' : {
display_form() ;
break ;
}
case 'validate' : {
validate() ;
break ;
}
default : {
display_form() ;
}
}

Le formulaire html ressemble donc à quelque chose du genre :

<form id="form" action="{SELF}" method="POST">
<input type="hidden" name="act" value="validate">

<label for="var1">Var 1</label><span class="renvoi">*</span>
<input type="text" name="var1" id="var1"><br>

<label for="var2">Var 2</label><span class="renvoi">*</span>
<input type="text" name="var2" id="var2"><br>

<label for="var3">Var 3</label>
<input type="text" name="var3" id="var3"><br>

<input type="submit" value="Valider">
</form>

{SELF} correspond à $_SERVER['PHP_SELF']
Les * signifient le caractère obligatoire des champs à remplir.
Le champs caché "act" permet d'utiliser le même script php pour
différentes actions.

--
Patrick


Avatar
Nicklas
Le Wed, 25 Feb 2004 00:52:46 +0000, John Gallet a écrit :

Bonjour/bonsoir,

Le sujet du moment : vous devez gérer un formulaire avec deux variables
obligatoires, une facultative. Si les deux obligatoires sont reçues, vous
faites un insert en sgbdr. A noter que l'une des variables est clef primaire
de la table histoire de compliquer un peu. Sinon vous regénérez le
formulaire en préremplissant les champs déjà saisis. Comment faites vous ça
proprement (avec séparation code / présentation maximale, sans pour autant
utiliser des templates, avec gestion d'erreurs élégantes pour l'internaute
etc...).



J'utilise cette méthode sur mon site :
http://www.progweb.com
Les sources de mon site sont disponibles dans la section Projet.


Pour moi, le but est de contrôler par PHP les données, puis
de réafficher le formulaire pré-rempli avec les champs en
rouge et des messages d'erreurs.

Je sépare mon code PHP de mon code HTML.
Mais je n'utilise pas de système de template, je trouve que cela
ne sert à rien. PHP est lui-même un moteur de template...

<?php
// Je récupère les variables ; j'utlise la lib :
// http://www.progweb.com/modules/cours/consult.php?own=4&numR
$request->setMode(GET | POST);
$action = $request->List('action', array('add', 'sup', 'mod'));
$name = $request->Text('name');
$mail = $request->Mail('mail');
$birthday = $request->CheckDate('birthday', 'FR', '');
$url = $request->URL('url');


function getStyle($error) {
if ($error)
echo "InputError";
else
echo "InputForm";
}


switch ($action) {
case 'add':
$flag = 0;

if (empty($name)) {
$error_name = true;
$flag = 1;
}

if (empty($mail)) {
$error_mail = true;
$flag = 1;
}

if (empty($birthday)) {
$error_birthday = true;
$flag = 1;
}

if ($flag == 1) {
$name = stripslashes($name);
$name = htmlentities($name);

$mail = stripslashes($mail);
$mail = htmlentities($mail);

$birthday = stripslashes($birthday);
$birthday = htmlentities($birthday);

$url = stripslashes($url);
$url = htmlentities($url);

$action = 'add';
}
else {
$birthday = $parse->date2en($birthday, '-');

$query = "INSERT INTO ....";
$result = mysql_query($query);

$action = 'addfin';
}

break;

default:
$url = 'http://';
}

include('page.html');
?>

Fichier page.html :

<? if ($action == 'addfin'): ?>
<p>Donnée ajoutée...
<? else: ?>
<? if (($action == 'add') && ($flag == 1)): >
<p>Veuillez corriger les champs en rouge...
<? endif; ?>

<form method='POST' action='page.php'>
<input type="hidden" name="action" value="add">
<p class="<?=getStyle($error_name)?>>Nom :
<input type="text" name="name" value="<?=$name?>">
<p class="<?=getStyle($error_mail)?>>Mail :
<input type="text" name="mail" value="<?=$mail?>">
<p class="<?=getStyle($error_birthday)?>>Naissance :
<input type="text" name="birthday" value="<?=$birthday?>">
<p class="<?=getStyle($error_url)?>>Page Web :
<input type="text" name="url" value="<?=$url?>"> <input type="submit"
name="Ajouter" value="Ajouter">
</form>
<? endif; ?>



Pour les champs select :
<select name="toto" ...>
<option value="1" <?=isSelected('toto', 1)?>>Text 1</option>
<option value="2" <?=isSelected('toto', 2)?>>Text 2</option>
<option value="abc" <?=isSelected('toto', 'abc')?>>Text abc</option>
</option>

De même j'ai isChecked pour les checkbox...

Les variables peuvent être de tout type.

function isChecked($name, $value) {
if (is_array($name)) {
if (in_array($value, $name))
return 'checked';
}
else {
if ($name == $value)
if (($name === 0) || ($value === 0))
return ((string) $name === (string) $value) ? 'checked' : '';
else
return 'checked';
}
}


function isSelected($name, $value) {
if (is_array($name)) {
if (in_array($value, $name))
return 'selected';
}
else {
if ($name == $value)
if (($name === 0) || ($value === 0))
return ((string) $name === (string) $value) ? 'selected' : '';
else
return 'selected';
}
}


--
Nicklas
ProgWeb - Développer son site Internet
http://www.progweb.com

Avatar
John Gallet
Bonsoir,

Pour moi, le but est de contrôler par PHP les données, puis
de réafficher le formulaire pré-rempli avec les champs en
rouge et des messages d'erreurs.


On est d'accord.

Je sépare mon code PHP de mon code HTML.
Mais je n'utilise pas de système de template, je trouve que cela
ne sert à rien. PHP est lui-même un moteur de template...



C'est une vision simplifiée mais ce que j'appelle couramment le "template du
pauvre"(c)(tm). je te rejoins sur le fait que, quelle que soit la manière
dont on le tourne, il faut bien nommer sa donnée et indiquer où on eut
qu'elle soit affichée dans le canevas html statique. Partant de là, la
solution la plus simple est de faire un <?php echo $nom_variable;?> là où il
faut dans le canevas html (je n'ai pas trop confiance dans la version courte
que tu utilises, j'ai eu des réultats "marrants" après qu'un infographiste
eut décidé d'ouvrir un fichier de ce genre sous son éditeur préféré, mais le
principe est rigoureusemen identique).

$query = "INSERT INTO ....";
$result = mysql_query($query);



Comment ça pas de gestion d'erreur ? Mais c'est mal ! ;-)

include('page.html');
?>
Fichier page.html :



NB pour les lecteurs qui se poseraient la question : là on par du principe
que l'extension .html est parsée par php, ce qui n'est pas le cas partout,
mais il suffit de nommer ce fichier page.php est c'est pareil.

if ($name == $value)
if (($name === 0) || ($value === 0))
return ((string) $name === (string) $value) ? 'checked' : '';


Moi j'aime pas jouer à vérifier aussi le type, mais c'est perso et aussi lié
à mes fonctions de filtrage.

a++
JG

Avatar
John Gallet
Bonsoir,

En haut de page, une constante qui contient l'url vers laquelle on se
dirige si la validation des variables du formulaire est OK


Qu'entends tu exactement par "validation des variables " ? Tu as déjà fait
l'insert demandé ou pas ?

// Attention, si l'un des champs est une clé primaire, il faut faire
// une requête pour vérifier l'existence éventuelle d'un record avec
// cette clé !



Non c'est idiot. Tu fais ton insert et si tu as une erreur duplicate key
c'est que l'enregistrement était là. C'est à ça que sert une clef unique,
empêcher les doublons, c'est son boulot. Ce SELECT est non seulement
inutile, mais comme en plus dans mysql tu n'es pas en transaction, son
résultat n'a aucune valeur.

// if valide
header('Location: '.NEXT_URL) ;


Si ton insert a été fait au dessus, pourquoi pas. Je suis contre ce type de
pratiques, que je remplace par require(NEXT_URL); mais si ça t'amuse de
générer des aller-retours entre le navigateur et le serveur, pourquoi pas.

<label for="var1">Var 1</label><span class="renvoi">*</span>
<input type="text" name="var1" id="var1"><br>


id ? Ca sert à quoi ça ?
Et les valeurs déjà saisies par l'internaute quand il en manque d'autres
obligatoires ?

Le champs caché "act" permet d'utiliser le même script php pour
différentes actions.


Oui tt à fait, classique.

a++
JG

Avatar
Christophe PEREZ
Le Wed, 25 Feb 2004 13:50:26 +0000, Patrick a écrit:

Pour préciser
[...]


Merci à toi pour l'exemple, et merci aussi (et surtout) à John pour
l'idée à renouveler.

Comme par hasard, je suis en plein dans le sujet depuis plusieurs jours.
Et cet exemple m'a donné de nouvelles idées, même si globalement c'est
un peu ce que j'essaye de faire.
Mais pour que tout soit automatisé, et que je puisse faire un minimum de
chose lors de l'ajout d'un nouveau formulaire, j'ai choisi la solution
d'avoir une table qui regroupera tous les champs de toutes les tables en
indiquant pour chacun :
- quel texte de label lui mettre, et s'il est vide, le champ n'apparaît
pas dans le formulaire
- l'ordre d'apparition dans le formulaire
- les codes de traitement de validation et le numéros de message d'erreur
associés.
ex : 1|123,2|12
le champ devra satisfaire aux tests 1 et 2 de ma fonction de contrôle de
saisie, et renverra le cas échant les codes d'erreur 123 et/ou 12 qui
sont stockés dans une autre table avec le message d'erreur associé
- le codage éventuel à appliquer lors de l'INSERT ou UPDATE (ex PASSWORD)
- Je récupère directement dans les informations de la table tout ce qui
est type de champ, longueur de champ, non nul ou pas...

Je me sers de cette même table pour générer de façon automatique les
listes (états) pour chaque table avec les informations supplémentaires
pour chaque champ :
- texte d'entête de colonne, si vide, n'apparaît pas dans l'état
- ordre d'apparition dans l'état
- ordre de tri à affecter : ASC, DESC ou vide
- un bouton éventuel à générer qui permet de passer en mode édition
pour cet enregistrement, selon les autorisations attribuées à
l'utilisateur.

Tout ceci ne répond évidemment pas à la problématique initiale puisque
ça serait beaucoup trop lourd pour un ou deux formulaires. Je l'exprime
juste pour éventuellement donner aussi des idées à d'autres.

--
Christophe PEREZ
Écrivez moi sans _faute !

Avatar
Harry Cover
On 25 Feb 2004 13:50:26 GMT, John Gallet
wrote:


Donne un exemple avec un template si tu veux, mais pour un pauvre formulaire
même à 30 champs, ça vaut pas le coup, enfin c'est mon avis et je le
partage. On va dire qu'on utilise ce que j'appelle "le template du pauvre"
(tm)(c)(jgallet 2001)
Oué. C'est sûr. Mais je n'aime pas trop bosser sur du code comme ca,

si ca grossit après, ca devient illisible.

En revanche, tiens, comment garderais-tu un champ d'un SELECT à la valeur
déjà saisie en utilisant un template ? Ca ca peut être intéressant à
montrer.
Bin tu fais :


<select name="user[]" size="8">
<!--#in user_list -->
<option value="{ id }" { selected }>{ nom }</option>
<!--#endin user_list -->
</select>

Mon système de template fonctionne avec des objets.
J'associe un objet à un template pour remplir ce dernier, sur le
principe suivant :

Variable du template --> variable de même nom dans l'objet.
Boucle du template --> méthode de même nom dans l'objet
(dans l'exemple ci-dessus, user_list est une boucle du template, et {
nom } une variable).

Tant que la méthode associée à la boucle retourne True, la boucle est
effectuée, et les variables qui sont dedans remplacées.
--> A chaque tour, je rajoute un <option>.

Quand ca m'intéresse, je spécifie dans la méthode : $this->selected "selected" ;
Du coup dans la boucle de mon template, j'ai un option selected qui
apparait.
La boucle n'est plus exécutée par le système de template dès qu'elle
retourne False.

Ca va, c'est pas trop condensé ? ;)

-Validation du formulaire --> une fonction qui controle les arguments
(anti SQL injection, etc...)
Râââh lovely, enfin un qui y pense. Ca fait plaisir.

On peut même imaginer faire une fonction qui teste les variables

provenant du formulaire à base de regexp mais c'est pas mon truc.


1 2