Dates, formulaires et MySQL

mercredi 11 avril 2007

Le Web devient de plus en plus "interactif". Les utilisateurs sont de plus en plus souvent sollicités par des sites qui récupèrent les informations personnelles qu'ils veulent bien communiquer. Ces sites sont construits autour de deux axes: récupérer et traiter les données, stocker les données. Pour ce qui est de récupérer et traiter les données, le plus facile et le plus rapide à mettre en oeuvre reste la solution JavaScript et PHP. Pour le stockage des données, dans la plupart des cas c'est le moteur de base de données MySQL qui s'impose. Dans la plupart des cas, certaines données sont des dates: une date d'anniversaire fournie par l'utilisateur, une date d'inscription enregistrée au moment de la première connexion de l'utilisateur, etc. Or, MySQL utilise un format de dates qui déroute totalement les utilisateurs. Comment, dès lors, présenter aux utilisateurs un format auquel ils sont habitués et à MySQL le format exigé pour l'enregistrement des données dans une colonne de type DATE ?

Le Web devient de plus en plus «interactif». Les utilisateurs sont de plus en plus souvent sollicités par des sites qui récupèrent les informations personnelles qu’ils veulent bien communiquer. Ces sites sont construits autour de deux axes: récupérer et traiter les données, stocker les données. Pour ce qui est de récupérer et traiter les données, le plus facile et le plus rapide à mettre en oeuvre reste la solution JavaScript et PHP. Pour le stockage des données, dans la plupart des cas c’est le moteur de base de données MySQL qui s’impose. Dans la plupart des cas, certaines données sont des dates: une date d’anniversaire fournie par l’utilisateur, une date d’inscription enregistrée au moment de la première connexion de l’utilisateur, etc. Or, MySQL utilise un format de dates qui déroute totalement les utilisateurs. Comment, dès lors, présenter aux utilisateurs un format auquel ils sont habitués et à MySQL le format exigé pour l’enregistrement des données dans une colonne de type DATE ?

1. Technologies et méthode employées

Dans notre exemple, les données sont collectées depuis un formulaire HTML par un script PHP (par l’intermédiaire de la variable globale $_POST). Ces données sont au préalable vérifiées grâce à un script JavaScript. Une fois vérifiées et collectées, elle doivent être enregistrées dans la base de données MySQL, grâce à un script PHP. Finalement, les données collectées doivent pouvoir être extraites de la base de données MySQL pour être affichées à l’écran par un script PHP.

Plus précisément, nous allons récupérer une date au format MM/JJ/AAAA, contrôler que ce format est respecté, la transformer au format AAAA-MM-JJ, et l’insérer dans la base de données. Ensuite, nous récupérerons la date au format AAAA-MM-JJ pour la reconvertir dans un format plus lisible par l’utilisateur et l’afficher à l’écran.

2. Contrôle du format de la date avec JavaScript.

Il existe deux méthodes pour contrôler les données. La «bonne vieille» méthode et la méthode élégante utilisant les expressions régulières (Regex). Nous allons détailler la «bonne vieille» méthode et faire une allusion rapide aux expressions régulières, qui seront développées par la suite lors de leur utilisation dans le script PHP.

a. Avec la «bonne vieille» méthode

La première chose à faire est de définir, dans le code HTML, le nom ou l’identifiant du champ qui contiendra la date. Ici, il s’agit d’un champ de type input qui porte le nom de date.

<input name=»date» type=»text»>

La deuxième chose à faire est invoquer le script JavaScript de contrôle dans le formulaire, pour que les données soient contrôlées quand l’utilisateur clique sur le bouton de soumission. Dans notre exemple, le nom du formulaire est soumettre et il utilise la méthode post. Les données sont transmises au code PHP en invoquant le fichier script.php. Enfin, avant soumission des données, la méthode JavaScript completude() est appelée, avec pour argument le formulaire lui-même.

<form name=»soumettre» method=»post» action=»script.php» onSubmit=»return completude(this);»>

Il faut ensuite définir la fonction completude() dans le code JavaScript. Dans un premier temps, on crée la fonction et on récupère le contenu du champ date créé ci-avant.

function completude(objet)
{
</p>

/* Ceci est le message qui sera affiché avec alert() si les données fournies ne conviennent pas. Pour l’instant, il est vide ca tout va bien */
var msg = «»;

/* On récupère le contenu du champ date */
var daten = objet.date.value; </div> <p>Une fois cela fait, il faut vérifier que l’information entrée dans le champ est bien une date, et que celle-ci est correctement formatée. Il faut pour cela procéder en 3 temps:
- vérifier qu’il y a bien deux délimiteurs slash (/) (la date s’écrit au format JJ/MM/AAAA)
- vérifier que le jour et le mois sont exprimés sur 2 chiffres et l’année sur 4 chiffres
- vérifier que le jour est compris entre 01 et 31, le mois entre 01 et 12, et l’année, par exemple, supérieure à 1900.

Première étape.
On brise la chaîne qui contient la date en plusieurs morceaux. Ces morceaux sont déterminés par le délimiteur spécifié, le slash dans notre exemple (/). Ce programme va parcourir la chaîne jusqu’à trouver le délimiteur, toutes les données avant le délimiteur formeront le premier membre (le jour). Puis, il va continuer à parcourir la chaîne juqu’à trouver un second délimiteur pour former le second membre (le mois). Finalement, ce qui restera sera le troisième membre (l’année).

/* On brise la chaîne… */
var temp = new Array();
temp = daten.split(‘/’);
</p>

/*
Le jour est désormais contenu dans la variable temp[0].
Le mois est désormais contenu dans la variable temp[1].
L’année est désormais contenue dans la variable temp[2].
*/

Si temp[2] est nul c’est qu’il n’y a pas d’année. Si temp[1] est nul, c’est qu’il n’y a pas de mois. Si temp[0] est nul c’est qu’il n’y a pas de jour. Dans tous les cas, la date n’est pas valable. Il faut donc qu’aucun des membres de temp ne soit nul.

/* Si le séparateur de date n’est pas «/»: afficher un message d’erreur */
if(temp[0] == null || temp[1] == null || temp[2] == null)
msg += «Veuillez formater la date au format indiqué: \n\nJJ/MM/AAAA \n\n JJ représente le jour sur 2 chiffres \n MM représente le mois sur 2 chiffres \n AAAA représente l’année sur 4 chiffres «;
</p>

// Sinon, la date a le bon séparateur («/»)
else
{
/* Pour améliorer la lisibilité du programme, on stocke le jour dans «j», le mois dans «m» et l’année dans «a». */
j = temp[0];
m = temp[1];
a = temp[2];
</div> <p>Deuxième étape
Le test passé avec succès, on peut continuer. On va maintenant contrôler que chaque membre a le bon nombre de chiffres. Le jour et le mois sont exprimés sur 2 chiffres, l’année sur 4 chiffres.

/* Contrôler que le jour, le mois et l’année sont bien à 2 et 4 chiffres. */
if(j.length < 2)
msg += «Format de date incorrect. \nVeuillez entrer le jour sur 2 chiffres.»;
if(m.length < 2)
msg += «Format de date incorrect. \nVeuillez entrer le mois sur 2 chiffres.»;
if(a.length < 4)
msg += «Format de date incorrect. \nVeuillez entrer l’année sur 4 chiffres.»;

</strong></div>

Troisième étape
Il ne reste plus qu’à contrôler que le jour est bien compris entre 01 et 31, le mois entre 01 et 12 et l’année, dans notre exemple, postérieure à 1900.

/* Contrôler que le jour est entre 01 et 31, le mois entre 01 et 12, l’année >1900 */
if(!(j >= 01 && j < = 31))
msg += «Format de date incorrect. \nLe jour doit être compris entre 01 et 31.»;
if(!(m >= 01 && m < = 12))
msg += «Format de date incorrect. \nLe mois doit être compris entre 01 et 12.»;
if(!(a >= 1900 && a < = 2100))
msg += «Format de date incorrect. \nL’année doit être comprise entre 1900 et 2100.»;

</strong></div>

Finition
Comme on l’a dit plus haut, cette fonction est appelée lors de la soumission du formulaire. En réalité, elle est appelée juste avant que le formulaire soit soumis, et elle a une influence sur le processus. Si elle retourne TRUE, le formulaire sera soumis. Si elle retourne FALSE, le formulaire ne sera pas soumis. Il faut donc maintenant définir si elle doit retourner TRUE ou FALSE.

Au début de la fonction, nous avons créé la variable vide msg. Si elle est restée vide, c’est qu’il n’y a pas de message d’erreur et tout va bien: on renvoie TRUE. Si elle n’est plus vide c’est qu’elle contient un message d’erreur: il faudra renvoyer FALSE et afficher ce message.

/* Envoyer le formulaire… */
if (msg == «») return(true);</p>

/* Ne pas l’envoyer… et afficher le message d’erreur */
else
{
alert(msg);
return(false);
}
</div> <p>b. Avec les expressions régulières

Tout ceci peut également être fait beaucoup plus rapidement, en utilisant les expressions régulières.

var daten = objet.date.value;
var pattern=»XXX»; /* v. plus bas, dans le code PHP, pour les expressions régulières */
var bool;
bool = pattern.test(daten); </p>

if(!bool)
msg += «Format de date incorrect….»;</strong> </div> <h2>3. Formater la date pour MySQL</h2> <p>a. Récupérer les données

Avant de formater la date, il faut la récupérer. Pour cela, on utilisera la variable $_POST. Pour plus d’informations à ce sujet, v. Construire une base de données simple avec PHP et MySQL. Une fois la date récupérer, on la passe à la méthode formater(), détaillée plus bas.


$date = $_POST[«date»];
formater($date,true);

b. La fonction formater(): principe

La fonction formater() a pour but de transformer la date du format JJ/MM/AAAA, lisible par l’utilisateur, vers le format AAAA-MM-JJ, exploitable par MySQL. Elle admet deux arguments. Le premier argument est la date à formater (une chaîne de caractères). Le second argument est une variable booléene, qui prend les valeurs TRUE ou FALSE. Cela nous permettra plus tard d’opérer la conversion dans le sens inverse (AAAA-MM-JJ vers JJ/MM/AAAA): selon que la valeur est TRUE ou FALSE, la conversion s’effectue dans un sens ou dans l’autre.

b. La fonction formater(): implémentation

Voici notre fonction:

function formater($date,$vers_mysql)
{</p>

// JJ/MM/AAAA => AAAA-MM-JJ
if($vers_mysql)
{
$pattern = «([0-9]{2})/([0-9]{2})/([0-9]{4})»;
$replacement = «$3-$2-$1»;
}

// AAAA-MM-JJ => JJ/MM/AAAA
else
{
$pattern = «([0-9]{4})-([0-9]{2})-([0-9]{2})»;
$replacement = «$3/$2/$1»;
}

return preg_replace($pattern, $replacement, $date);
}
</div> <p>Il convient d’expliquer cette fonction. Trois choses sont remarquables: la fonction preg_replace(), la variable $pattern et la variable $replacement.

La fonction preg_replace() telle qu’utilisée ici analyse $date au regard de $pattern, qui décrit la structure de la date, puis construit et renvoie une nouvelle chaîne, avec les éléments de $date, formatée selon les spécifications de $replacement.

La variable $pattern est une expression régulière qui se décompose ainsi: un bloc A, un slash, un bloc B, un slash, un bloc C: A/B/C. Le bloc A est le suivant: ([0-9]{2}). Cela signifie deux chiffres compris entre 0 et 9. Le bloc B est identique au bloc A. Le bloc C est légèrement différent, puisqu’il correspond à l’année: ([0-9]{4}) signifie quatre chiffres compris entre 0 et 9.

Autrement dit, le programme comprend l’expression ([0-9]{2})/([0-9]{2})/([0-9]{4}) comme un être humain comprendrait la phrase suivante: un élément de 2 chiffres entre 0 et 9, une barre oblique, un élément de 2 chiffres entre 0 et 9, une barre oblique, un élément de 4 chiffres entre 0 et 9.

On l’aura compris, [0-9] signifie entre 0 et 9 et {2} signifie deux chiffres. Les parenthèses servent à délimiter les blocs (A, B et C dont on parlait plus haut). Notre but final est de changer l’ordre de ces blocs: de A/B/C vers C-B-A.

La variable $replacement définit le nouveau placement des blocs: $3-$2-$1. La parenté de cette expression réelle avec l’exemple théorique C-B-A est évident. Cette expression demande au programme de placer le troisième bloc, puis un tiret, puis le deuxième bloc, puis un tiret, et enfin le premier bloc.

Voilà comment passer de JJ/MM/AAAA vers AAAA-MM-JJ. Pour effectuer l’opération inverse, le raisonnement est le même, mais les expressions régulières changent un peu (v. le code ci-dessus).

4. Insérer la date dans la base de données MySQL et l’en extraire

Pour insérer la date dans MySQL, il suffira d’une instruction SQL basique: INSERT INTO… Pour plus de précisions, v. Construire une base de données simple avec PHP et MySQL.

Pour extraire la date et l’afficher à l’écran, il suffira de réutiliser la fonction formater(), en réglant cette fois la variable booléenne sur FALSE.



while($data = mysql_fetch_assoc($sql))
$date = formater($data[«date»],false);

echo «La date est: $date»;

Pour aller plus loin

Nous avons utilisé ici le format européen de dates: JJ/MM/AAAA. Mais aux Etats-Unis, c’est le format américain qui est utilisé: MM/JJ/AAAA. Une solution bien pratique serait de détecter la langue du visiteur pour écrire la date dans le format auquel il est habitué.

Pour déterminer la langue du visiteur, on peut dans la majorité des cas se fier à la langue de son navigateur. Cette dernière s’obtient grâce à une variable globale PHP: $_SERVER[«HTTP_ACCEPT_LANGUAGE»]. Cette variable contiendra en principe un tableau indiquant quelles langues sont gérées par le navigateur. La première, exprimée en 2 lettres, est la langue principale. Il s’agit de «fr» pour le français, de «es» pour l’espagnol et de «en» pour l’anglais.

/* $langue est «fr» pour le français, «en» pour l’anglais */
$langue = substr($_SERVER[«HTTP_ACCEPT_LANGUAGE»], 0, 2);

On peut ensuite rajouter un argument pour la langue à notre fonction formater():

function formater($date,$vers_mysql,$langue)
{
if($langue == «en»)
{
if($vers_mysql)
// regex 1 - format américain
else
//regex 2 - format américain
{
else
{
if($vers_mysql)
// regex 1 - format européen
else
//regex 2 - format européen
}
}