168 Chapitre 3. Programmation objet 2. la représentation de la table par un tableau HTML ; 3. la production d’un formulaire de saisie ou de mise à jour ; 4. des contrôles, avant toute mise à jour, sur le type ou la longueur des données à insérer ; 5. enfin la production d’une interface de consultation, saisie ou mise à jour semblable à celle que nous avons étudiée page 78. La classe IhmBD (pour « Interface homme-machine et Bases de Données ») est une implantation de toutes ces fonctionnalités. Elle permet d’obtenir sans aucun effort, par simple instanciation d’un objet suivi d’un appel de méthode, une inter- face complète sur une table de la base. Bien entendu, cette interface peut s’avérer insatisfaisante du point de vue de l’ergonomie, de la présentation, ou du respect des règles particulières de gestion pour une table donnée. Dans ce cas on peut soit utiliser certaines méthodes pour régler des choix de présentation, soit définir une sous-classe spécialisée. Tous ces aspects sont développés dans ce qui suit. Cette classe est un bon exemple du processus d’abstraction mis en œuvre cou- ramment en programmation objet, et visant à spécifier de manière générale un com- portement commun à de nombreuses situations (ici l’interaction avec une base de données). Le bénéfice de ce type de démarche est double. En premier lieu on obtient des outils pré-définis qui réduisent considérablement la réalisation d’applications. En second lieu on normalise l’implantation en décrivant à l’avance toutes les méthodes à fournir pour résoudre un problème donné. Tout cela aboutit à une économie importante d’efforts en développement et en maintenance. Dernier avantage: la description de la classe va nous permettre de récapituler tout ce que nous avons vu sur les techniques d’accès à MySQL (ou plus généralement à une base de données) avec PHP. 3.4.1 Utilisation Dans sa version la plus simple, l’utilisation de la classe est élémentaire : on instancie un objet en indiquant sur quelle table on veut construire l’interface, on indique quelques attributs de présentation, et on appelle la méthode genererIHM().Les quelques lignes de code qui suivent, appliquées à la table Carte qui a déjà servi pour la mini-application « Prise de commandes au restaurant » (voir page 99), suffisent. Exemple 3.10 exemples/ApplClasseIhmBD.php : Application de la classe ImhBD. <?xml version=" 1.0 " encoding=" iso −8959−1"?> <!DOCTYPE html PUBLIC " −/ /W3C / / DTD XHTML 1 . 0 S t r i c t / / EN " "http ://www.w3. org/TR/xhtml1/DTD/xhtml1− strict .dtd"> <html xmlns="http ://www.w3. org/1999/xhtml" xml:lang=" fr " > <head> <title >Création d’un formulaire </title > <link rel=’stylesheet ’ href="films .css" type="text/ css"/> </head> 3.4 La classe IhmBD 169 <body> <?php require_once ("BDMySQL.php") ; require_once ("IhmBD.php") ; require (" Normalisation .php") ; require (" Connect .php") ; // Normalisation des entrées HTTP Normalisation() ; try { // Connexion à la base $bd = new BDMySQL (NOM, PASSE, BASE, SERVEUR) ; // Creation de l ’ interface sur la table Carte $ihm = new IhmBD ( " Cart e " , $bd ) ; // Les en−têtes (pas obligatoire: le nom du champ sert d’en− // tête sinon) $ihm−>setEntete("id_choix" , "Numéro du plat"); $ihm−>setEntete("libelle" , "Libellé du plat"); $ihm−>setEntete("type" , "Type du plat"); // Génération de l ’ interface echo $ihm−>genererIHM($_REQUEST) ; } catch (Exception $exc) { echo "<b>Erreur rencontrée:</b> " . $exc−>getMessage() . "\n" ; } ?> Bien entendu on réutilise la classe BDMySQL qui fournit tous les services néces- saires pour accéder à la base, de même que la classe Tableau nous servira pour les tableaux et la classe Formulaire pour les formulaires. Notez que l’utilisation d’une classe normalisée pour accéder à la base de données signifie que tout ce qui est décrit ci-dessous fonctionne également avec un SGBD autre que MySQL, en instanciant simplement un objet bd servant d’interface avec ce SGBD et conforme aux spécifications de la classe abstraite BD (voir page 130). La figure 3.7 montre l’affichage obtenu avec le script précédent. Il s’agit de bien plus qu’un affichage d’ailleurs : on peut insérer de nouvelles lignes, ou choisir de modifier l’une des lignes existantes à l’aide du formulaire. L’ajout de la fonction de destruction est, comme un certain nombre d’autres fonctionnalités, laissée en exercice au lecteur. On obtient donc un outil en partie semblable à ce qu’offre phpMyAdmin.La structure de la table est récupérée de MySQL (ou de tout autre SGBD) et utilisée pour produire le formulaire, le tableau, les contrôles, etc. Bien entendu phpMyAdmin propose beaucoup plus de choses, mais il existe une différence de nature avec la classe IhmBD. Alors que phpMyAdmin est un outil intégré, nos objets fournissent des briques 170 Chapitre 3. Programmation objet Figure 3.7 — Affichage de l’interface sur la table Carte. logicielles qui peuvent être intégrées dans toute application utilisant les méthodes publiques énumérées dans la table 3.9. Elles constituent une panoplie des accès à une table, à l’exception de l’ouverture d’un curseur pour accéder à un sous-ensemble des lignes. Tableau 3.9 — Les méthodes publiques de la classe IhmBD Méthode Description formulaire (action, ligne ) Renvoie un formulaire en saisie ou en mise à jour sur une ligne. insertion (ligne ) Insère d’une ligne. maj (ligne ) Met à jour d’une ligne. tableau (attributs ) Renvoie un tableau HTML avec le contenu de la table. setEntete (nomAttribut, valeur ) Affecte un en-tête descriptif à un attribut. chercheLigne (ligne, format ) Renvoie une ligne recherchée par sa clé, au format tableau associatif ou objet. genererIHM (paramsHTTP ) Produit une interface de consultation/mise à jour, basée sur les interactions HTTP. REMARQUE – Ce besoin de disposer d’outils génériques pour manipuler les données d’une base relationnelle à partir d’un langage de programmation, sans avoir à toujours effectuer répétitivement les mêmes tâches, est tellement répandu qu’il a été « normalisé » sous le nom d’Object-Relational Mapping (ORM) et intégré aux frameworks de développement tel que celui 3.4 La classe IhmBD 171 présenté dans le chapitre 9. La classe hmBD est cependant légèrement différente puisqu’elle permet de générer des séquences de consultation/saisie/mise à jour, ce que les outils d’ORM ne font généralement pas. Ces méthodes peuvent être utilisées individuellement ou par l’intermédiaire des interactions définies dans la méthode genererIHM(). Elles peuvent aussi être rédéfinies ou spécialisées. On aimerait bien par exemple disposer d’une liste dérou- lante pour le type de plat dans le formulaire de la table Carte. Il suffit alors de définir une sous-classe IhmCarte dans laquelle on ne ré-implante que la méthode formulaire(). Toutes les autres méthodes héritées de la super-classe, restent donc disponibles. 3.4.2 Implantation Voyons maintenant l’implantation de la classe. Tout repose sur la connaissance du schéma de la table, telle qu’elle est fournie par la méthode schemaTable de la classe BD. Rappelons (voir page 132) que cette méthode renvoie un tableau associatif avec, pour chaque attribut de la table, la description de ses propriétés (type, longueur, participation à une clé primaire). Ce tableau a donc deux dimensions : 1. la première est le nom de l’attribut décrit ; 2. la seconde est la propriété, soit type,soitlongueur,soitcle_primaire,soit enfin not_null. Dans l’exemple de la table Carte, on trouvera dans le tableau décrivant le schéma un élément [’id_choix’][’type’] avec la valeur integer,unélément [’id_choix’][’cle_primaire’] avec la valeur true,etc. REMARQUE – La classe ne fonctionne que pour des tables dotées d’une clé primaire, autrement dit d’un ou plusieurs attributs dont la valeur identifie une ligne de manière unique. La présence d’une clé primaire est de toute façon indispensable : voir le chapitre 4. Voici le début de la classe. On énumère quelques constantes locales, puis des propriétés dont l’utilité sera détaillée ultérieurement, et enfin le constructeur de la classe. class IhmBD { // −−−− Partie privée : les constantes et les variables const INS_BD = 1; c o ns t MAJ_BD = 2 ; c ons t DEL_BD = 3 ; const EDITER = 4; protected $bd , $nomScript , $nomTable , $schemaTable , $entetes ; // Le constructeur function __construct ($nomTable , $bd, $script="moi") { 172 Chapitre 3. Programmation objet // Initialisation des variables privées $this−>bd = $bd ; $this−>nomTable = $nomTable ; if ($script == "moi") $this−>nomScript = $_SERVER[ ’PHP_SELF ’ ] ; else $this−>nomScript = $script ; // Lecture du schéma de la table $this−>schemaTable = $bd−>schemaTable($nomTable) ; // Par défaut , les textes des attributs sont leurs noms foreach ($this−>schemaTable as $nom => $options) $this−>entetes [$nom] = $nom; } Le constructeur prend en entrée un nom de table, un objet de la classe BD (poten- tiellement également instance d’une sous-classe de BD : BDMySQL, BDPostgreSQL, BDSQLite, etc.) et le nom du script gérant l’interface avec la table. On com- mence par copier ces données dans les propriétés de l’objet pour les conserver durant toute sa durée de vie 5 . On recherche également le schéma de la table grâce à l’objet bd, et on le stocke dans la propriété schemaTable.Silatable n’existe pas, l’objet bd lèvera en principe une exception qu’on pourrait « attraper » ici. REMARQUE – On reçoit un objet, bd, passé par référence, alors que toutes les autres variables sont passées par valeur (comportement adopté depuis PHP 5). On stocke également une référence à cet objet avec l’instruction : $this−>bd = $bd ; L’opérateur d’affectation, pour les objets, n’effectue pas une copie comme pour tous les autres types de données, mais une référence. La variable $this->bd et la variable $bd référencent donc le même objet après l’affectation ci-dessus (voir page 61 pour la présentation des références). Il s’ensuit que deux codes indépendants vont travailler sur le même objet, ce qui peut parfois soulever des problèmes. Le script appelant a en effet instancié $bd et peut à bon droit estimer que l’objet lui appartient et qu’il peut en faire ce qu’il veut. Un objet de la classe IhmBD a lui aussi accès à cet objet et va le conserver durant toute sa durée de vie. Chacun peut effectuer des opérations incompatibles (par exemple fermer la connexion à la base) avec des résultats potentiellement dangereux. On pourrait effectuer une véritable copie de l’objet avec l’opérateur clone : $this−>bd = cl o n e $bd ; On s’assure alors qu’il n’y aura pas de problème posé par le partage d’un même objet, le prix (modique) à payer étant l’utilisation d’un peu plus de mémoire, et une opération de copie. 5. Par défaut, on utilise le script courant, où l’objet aura été instancié, et dénoté $_SERVER[’PHP_SELF’]. . appelant a en effet instancié $bd et peut à bon droit estimer que l’objet lui appartient et qu’il peut en faire ce qu’il veut. Un objet de la classe IhmBD a lui aussi accès à cet objet et va le conserver. sur une table de la base. Bien entendu, cette interface peut s’avérer insatisfaisante du point de vue de l’ergonomie, de la présentation, ou du respect des règles particulières de gestion pour. programmation objet, et visant à spécifier de manière générale un com- portement commun à de nombreuses situations (ici l’interaction avec une base de données). Le bénéfice de ce type de démarche est