328 Chapitre 8. XML <nom>Willis</nom> <annee_naissance>1955</annee_naissance> <Film> <titre>Pi`ege de cristal</titre> <annee>1988</annee> <code_pays>USA</code_pays> <genre>Action</genre> <nom_role>McClane</nom_role> </Film> <Film> <titre>Pulp fiction</titre> <annee>1994</annee> <code_pays>USA</code_pays> <genre>Action</genre> <nom_role>Butch Coolidge</nom_role> </Film> </Acteur> </Acteurs> Cette fois, en supposant que le point d’accès est toujours un acteur, on a toutes les informations relatives à cet acteur dans le même sous-arbre, ce qui va permettre d’y accéder efficacement et simplement. On voit en revanche que si on souhaite prendre comme point d’accès un film, les informations utiles sont réparties un peu partout dans l’arbre, et que leur reconstitution sera plus difficile. La base de données que nous utilisons dans nos exemples est très simple. Il est clair que pour des bases réalistes présentant quelques dizaines de tables, la conception d’un schéma XML d’exportation doit faire des compromis entre l’imbrication des données et la conservation des correspondances clé primaire/clé étrangère sous forme de lien de navigation dans le document XML. Tout dépend alors des besoins de l’application, de la partie de la base qu’il faut exporter, et des chemins d’accès privilégiés aux informations qui seront utilisés dans l’exploitation du document. 8.2.2 Application avec PHP La transformation d’une table MySQL en document XML est extrêmement simple puisqu’il suffit de créer une chaîne de caractères au format approprié. Une approche directe mais fastidieuse consiste à agir au cas par cas en engendrant « à la main » les balises ouvrante et fermante et leur contenu. Comme toujours il faut essayer d’être le plus générique possible : la fonction présentée ci-dessous prend un tableau associatif contenant une liste (nom, valeur ) et crée une chaîne XML. Cette chaîne est un élément dont le nom est passé en paramètre (si la chaîne vide est passée pour le nom, seul le contenu de l’élément, sans les balises ouvrante et fermante, est renvoyé). 8.2 Export de données XML 329 REMARQUE – Il faut, comme en HTML, être attentif à éviter d’introduire des caractères réservés comme <, >, ’, " ou & dans les contenus XML. Le traitement par la fonction htmlSpecialChars() qui remplace ces caractères en appels d’entités convient parfaite- ment. L’élément créé est précédé de tabulations afin de faciliter une mise en forme claire du document final, comme nous le montrerons plus loin. Enfin la représentation du tableau peut, au choix, reposer sur des attributs ou des éléments. Voici le code de la fonction, qui est techniquement une méthode statique de la classe utilitaire EchangeXML. static function tableauVersXML ($tableau=array() , $nom_element=" l i g n e " , $ nb _ t a b =0 , $ f or ma t= s e l f : : ELEMENTS) { // Création d ’une chaîne avec le nombre $nb_tab de // tabulations $tabs="" ; for ( $i=0; $i < $nb_tab ; $i++) $tabs .= "\t " ; $chaine_XML = $attrs = ""; // Mise en forme en fonction du format demandé if ( $ f or ma t == s e l f : : ELEMENTS) { // On crée simplement un élément XML pour chaque attribut // de la table , et on concatène les éléments foreach ( $ta b le a u as $nom => $v al ) { // On retire les retours à la ligne du résumé if ($nom == "resume") $val = str_replace ("\n" , "\n\t " , $val) ; // On place l ’identifiant comme un attribut if ($nom=="id") $attrs .=" $nom=’$val ’ "; // Pour tous les autres on crée un élément if ($nom != "id_realisateur" and $nom!="id" and !empty( $val)) { $chaine_XML = $chaine_XML . $tabs . " <$nom>" . htmlSpecialChars($val) . " </$nom>\n" ; } } // La chaîne obtenue est le contenu de l ’élément $nom_element if (!empty($nom_element)) { $chaine_XML = "$tabs<$nom_element $attrs >\ n$chaine_XML$tabs </$nom_element>\n" ; } } else { // On crée un seul élément avec des attributs XML if ( is_array ($tableau)) { foreach( $t a bl e au as $nom => $va l ) { $chaine_XML .= " $nom=\"" . htmlSpecialChars($val) . " \" " ; 330 Chapitre 8. XML } } $chaine_XML = "$tabs <$nom_element $chaine_XML/>\n" ; } return $chaine_XML; } Les commentaires indiquent les étapes de cette conversion vers XML, qui ne pré- sente aucune difficulté conceptuelle. Maintenant il devient très facile de transformer une base en document XML. Notre outil d’export (voir la copie d’écran page 318) offre un formulaire permettant à l’utilisateur de saisir des critères de recherche pour des films de la base. À partir de ces critères une requête est créée (on réemploie bien entendu la fonction Util::creerRequetes() déjà utilisée pour rechercher les films à noter), exécutée, et chaque film est mis sous la forme d’un élément XML auquel on ajoute le metteur en scène et les acteurs. Voici le script complet de l’action export dans le contrôleur XML : function export () { // Pour créer un fichier par film $multi_files=false ; // Création de la requête SQL en fonction des critères $requete = Util :: creerRequetes ($_POST, $this−>bd ) ; $resultat = $this−>bd−>execRequete ($requete ) ; // On parcourt les films et on les transforme en XML $document = " " ; $nbFilms = 0; while ($film = $this−>bd−>ligneSuivante ($resultat)) { // Mise en forme du film $film_XML = EchangeXML : : tableauVersXML ( $film , "") ; // Mise en forme du metteur en scène $mes = Util :: chercheArtisteAvecID( $film [ ’ id_realisateur ’] , $this−>bd , FORMAT_TABLEAU) ; $film_XML .= EchangeXML : : tableauVersXML ($mes , " rea li s a t eu r " , 1 , EchangeXML : : ELEMENTS) ; // Ajout des acteurs et de leur rôle $ r eq _a ct e u r s = "SELECT id , prenom , nom , an ne e _ n ai ss a nc e , nom_role " . "FROM Artiste A, Role R " . "WHERE A.id = R. id_acteur AND R. id_film =’{$film [ ’ id ’]} ’ " ; $res_acteurs = $this−>bd−>execRequete($req_acteurs); while ($role = $this−>bd−>ligneSuivante($res_acteurs)) { $film_XML .= EchangeXML :: tableauVersXML ( $role , " Acteur " , 1 , EchangeXML : : ELEMENTS) ; 8.2 Export de données XML 331 } // On place le contenu dans la balise <Film> $document .= " <Film>\n" . $film_XML . "\n </Film>\n" ; $nbFilms++; } // On envoie l ’en− t ê t e HTTP, e t l e p r o l o g u e d u d o c u m e n t XML Header ("Content−type : text /xml"); echo "<?xml version=\"1.0\" encoding=\"iso−8859−1\"?>\n\n" ; // Mise en forme selon le choix de l ’utilisateur if ($_POST [ ’ fo rma t ’ ] == "XML" ) { // On sort le XML brut echo "<Films >\n$document </ Films >\n" ; ; } else { // On applique une transformation XSLT. Il suffit d’ajouter // une instruction pour que le navigateur en tienne compte // et applique la transformation Film. xsl echo "<?xml−stylesheet href = ’./ xsl /Film. xsl ’ type=’text/ xsl ’?>\n" . "<Films >\n$document </ Films >\n" ; ; } } Quand le format choisi est XML, le document renvoyé est déclaré de type MIME text/xml pour qu’il soit affiché sous une forme présentable dans le navigateur. En jouant sur le type MIME on pourrait également forcer le téléchargement du document sur la machine du client application/force-download). Si on choisit le format HTML, le même document est transmis, mais avec une instruction de traitement qui demande au navigateur d’appliquer une transformation XSLT. Nous y revenons en fin de chapitre, page 348. Vous pouvez directement utiliser ce script sur notre site pour récupérer un ou plusieurs films en XML. Voici par exemple le résultat obtenu pour Kill Bill. Exemple 8.10 KillBill.xml : Exemple de document produit par le script précédent <?xml version="1.0" encoding="ISO-8859-1"?> <Films> <Film> <titre>Kill Bill</titre> <annee>2003</annee> <code_pays>USA</code_pays> <genre>Drame</genre> <resume>Au cours d’une c´er´emonie de mariage en plein d´esert, un commando fait irruption dans la chapelle et tire sur les convives. Laiss´ee pour morte, la Mari´ee enceinte retrouve ses esprits apr`es un coma de quatre ans. Celle qui a auparavant exerc´e les fonctions de tueuse `a gages au sein du D´etachement International des Vip`eres Assassines n’a alors plus qu’une 332 Chapitre 8. XML seule id´ee en t^ete : venger la mort de ses proches en ´eliminant tous les membres de l’organisation criminelle, dont leur chef Bill qu’elle se r´eserve pour la fin.</resume> <Realisateur nom="Tarantino" prenom="Quentin" annee_naissance="1963" /> <Acteur prenom="Uma" nom="Thurman" annee_naissance="1970" nom_role="La mari´ee, alias "Black Mamba"" /> <Acteur prenom="Lucy" nom="Liu" annee_naissance="1968" nom_role="O-Ren Ishii" /> <Acteur prenom="David" nom="Carradine" annee_naissance="1936" nom_role="Bill" /> <Acteur prenom="Michael" nom="Madsen" annee_naissance="1958" nom_role="Budd / Sidewinder" /> <Acteur prenom="Daryl" nom="Hannah" annee_naissance="1960" nom_role="Elle Driver" /> </Film> </Films> 8.3 IMPORT DE DONNÉES XML DANS MySQL L’opération inverse, l’import d’un document XML dans une base de données MySQL, est un peu plus difficile. Au lieu de s’appuyer sur SQL pour récupérer les données dans la base, il faut utiliser un parseur de documents XML qui va analyser la structure du document et permettre d’accéder à ses différents composants. Les parseurs XML s’appuient sur deux modèles possibles de traitement d’un document, connus respectivement sous les acronymes SAX (Simple API for XML) et DOM (Document Object Model). Le modèle de traitement de SAX consiste à parcourir le document linéairement, et à déclencher des fonctions à chaque fois qu’une des catégories syntaxiques (balises ouvrantes, fermantes, texte, instructions de traitement, etc.) constituant un document XML est rencontrée. . permettant à l’utilisateur de saisir des critères de recherche pour des films de la base. À partir de ces critères une requête est créée (on réemploie bien entendu la fonction Util::creerRequetes(). étrangère sous forme de lien de navigation dans le document XML. Tout dépend alors des besoins de l’application, de la partie de la base qu’il faut exporter, et des chemins d’accès privilégiés. Création de la requête SQL en fonction des critères $requete = Util :: creerRequetes ($_POST, $this−>bd ) ; $resultat = $this−>bd−>execRequete ($requete ) ; // On parcourt les films et on