288 Chapitre 7. Production du site $ r e q u e t e = "DELETE FROM SessionWeb " . " WHERE id_session=’{$this−>session−>id_session } ’"; $resultat = $this−>bd−>execRequete ($requete); $this−>session = null ; $this−>vue−>contenu .= "Vous êtes maintenant déconnecté !\n" ; } else $this−>vue−>contenu = "Vous n’êtes pas encore connecté !\n"; // Rafraichissement de la partie du contenu qui montre soit // un formulaire de connexion , soit un lien de déconnexion $this−>statutConnexion () ; echo $this−>vue−>render("page") ; } Il se peut que logout() ne soit pas appelé par un internaute qui a simplement quitté le site sans passer par logout, et que l’information sur la session, bien que devenue invalide, reste dans la base. On peut au choix la garder à des fins statistiques, ou nettoyer régulièrement les sessions obsolètes. Figure 7.1 — Page d’accueil après identification d’un internaute 7.2 Recherche, présentation, notation des films 289 7.2 RECHERCHE, PRÉSENTATION, NOTATION DES FILMS Nous en arrivons maintenant aux fonctionnalités principales du site WEBSCOPE,à savoir rechercher des films, les noter et obtenir des recommandations. Tout ce qui suit fait partie du contrôleur Notation. Nous utilisons explicitement des requêtes SQL pour simplifier l’exposé, une amélioration possible étant de suivre scrupuleusement le MVC en définissant des modèles. 7.2.1 Outil de recherche et jointures SQL La recherche repose sur un formulaire, affiché dans la figure 7.2, qui permet d’entrer des critères de recherche. Ces critères sont : • le titre du film ; • le nom d’un metteur en scène ; • le nom d’un acteur ; • le genre du film ; • un intervalle d’années de sortie. Figure 7.2 — Formulaire de recherche des films Les quatre premiers champs ont chacun comme valeur par défaut «Tous », et l’intervalle de date est fixé par défaut à une période suffisamment large pour englober tous les films parus. De plus, on accepte une spécification partielle du titre ou des noms. Si un internaute entre « ver », on s’engage à rechercher tous les films contenant cette chaîne. Voici le formulaire, produit avec la classe Formulaire dans le cadre d’une méthode privée du contrôleur Notation. On aurait pu aussi créer un template avec 290 Chapitre 7. Production du site le code HTML et y injecter la liste des genres, qui est la seule partie dynamique provenant de la base. Les champs sont groupés par trois, et affichés dans deux tableaux en mode horizontal. private function formRecherche () { // Recherche de la liste des genres $resultat = $this−>bd−>ex ecRequet e ( "SELECT code FROM Genre " ) ; $genres [ "Tous" ] = "Tous" ; while ($g=$this−>bd−>objetSuivant( $resultat )) $genres[$g−> code ] = $g−>code ; // Création du formulaire $form = new Formulaire ("POST" , "? ctrl=notation& action= recherche") ; $form−> d e b u t T a b l e ( F o r m u l a i r e : : HORIZONTAL) ; $form−>champTexte ("Titre du film" , " titre " , "Tous" , 20); $form−>champTexte ("Metteur en scène" , "nom_realisateur" , "Tous " , 20) ; $form−>champTexte("Acteur" , "nom_acteur" , "Tous" , 20) ; $form−>finTable() ; $form−>ajoutTexte("<br/>"); $form−> d e b u t T a b l e ( F o r m u l a i r e : : HORIZONTAL) ; $form−>champListe ("Genre" , "genre" , "Tous" , 3, $genres ) ; $form−>champTexte ("Année min. " , "annee_min" , 1800, 4) ; $form−>champTexte ("Année max. " , "annee_max" , 2100, 4) ; $form−>finTable() ; $form−>champValider (" Rechercher" ," rechercher ") ; return $form−>formulaireHTML() ; } Requête sur une table Dans le cas où les champs nom_realisateur ou nom_acteur restent à « Tous », il ne faut pas les prendre en compte. Les critères de recherche restant font tous référence à des informations de la table Film. On peut alors se contenter d’une requête SQL portant sur une seule table, et utiliser la commande LIKE pour faire des recherches sur une partie des chaînes de caractères (voir Exemple 1.10, page 43). Voici la requête que l’on peut utiliser. SELECT titre , annee, code_pays , genre , id_realisateur FROM Film WHERE titre LIKE ’%$titre%’ AND annee BETWEEN ’ $annee_min ’ AND ’$annee_max ’ AND genre LIKE ’$genre ’ 7.2 Recherche, présentation, notation des films 291 Jointures Supposons maintenant que la variable $nom_realisateur ne soit pas égale à « Tous ». Il faut alors tenir compte du critère de sélection sur le nom du metteur en scène pour sélectionner les films et on se retrouve face à un problème pas encore abordé jusqu’à présent : effectuer des ordres SQL impliquant plusieurs tables. SQL sait très bien faire cela, à condition de disposer d’un moyen pour rap- procher une ligne de la table Film de la (l’unique) ligne de la table Artiste qui contient les informations sur le metteur en scène. Ce moyen existe : c’est l’attribut id_realisateur de Film qui correspond à la clé de la table Artiste, id. Rapprocher les films de leur metteur en scène consiste donc, pour une ligne dans Film, à prendre la valeur de id_realisateur et à rechercher dans Artiste la ligne portant cet id. Voici comment on l’exprime avec SQL. SELECT titre , annee , code_pays , genre , id_realisateur FROM Film , Artiste WHERE titre LIKE ’%$ t i t r e%’ AND nom LIKE ’%$nom_realisateur%’ AND annee BETWEEN $annee_min AND $annee_max AND genre LIKE ’$genre ’ AND id_realisateur = Artiste.id Ce type d’opération, joignant plusieurs tables, est désigné par le terme de jointure. La syntaxe reste identique, avec une succession de clauses SELECT-FROM-WHERE, mais le FROM fait maintenant référence aux deux tables qui nous intéressent, et le critère de rapprochement de lignes venant de ces deux tables est indiqué par l’égalité id_realisateur = id dans la clause WHERE. Les attributs auxquels on peut faire référence, aussi bien dans la clause WHERE que dans la clause SELECT, sont ceux de la table Film et de la table Artiste.Dansce premier exemple, tous les attributs ont des noms différents et qu’il n’y a donc aucune ambiguïté à utiliser l’attribut nom ou annee sans dire de quelle table il s’agit. MySQL sait s’y retrouver. Prenons maintenant le cas où la variable $nom_realisateur est égale à «Tous », tandis qu’un critère de sélection des acteurs a été spécifié. Le cas est un peu plus complexe car pour rapprocher la table Film de la table Artiste, il faut impliquer également la table Role qui sert d’intermédiaire (voir chapitre 4, page 195). Voici la requête SQL effectuant la jointure. SELECT Film. titre , annee, code_pays , genre , id_realisateur FROM Film , Artiste , Role WHERE Film . titre LIKE ’%$ t i t r e%’ AND nom LIKE ’%$nom_acteur%’ AND annee BETWEEN ’$annee_min ’ AND ’$annee_max ’ AND genre LIKE ’$genre ’ AND id_acteur = Artiste . id AND R o l e .id_film = Film. id 292 Chapitre 7. Production du site Comme dans le cas de la jointure entre Film et Artiste pour rechercher le metteur en scène, la jointure entre ces trois tables se fonde sur les attributs communs qui sont : 1. les attributs id et id_film dans Film et Role ; 2. les attributs id et id_acteur dans, respectivement, Acteur et Role. Il y a ambiguïté sur id puisque MySQL ne peut pas déterminer, quand on utilise cet attribut, si on fait référence à Film ou à Artiste. Pour lever cette ambiguïté, on préfixe donc le nom de l’attribut par le nom de la table d’où il provient. Dans le cas le plus général, l’utilisateur entre une valeur pour le metteur en scène et une pour le nom de l’acteur. En indiquant par exemple « itch » dans le champ nom_realisateur et « ewa » dans le champ nom_acteur, on devrait obtenir (au moins) le film Vertigo, dirigé par Alfred Hitchcock, et joué par James Stewart. Il faut donc à la fois faire la jointure Film-Artiste pour le metteur en scène, et Film-Role-Artiste pour les acteurs. On recherche en fait, simultanément, deux lignes dans la table Artiste, l’une correspondant au metteur en scène, l’autre à l’acteur. Tout se passe comme si on effectuait une recherche d’une part dans une table contenant tous les acteurs, d’autre part dans une table contenant tous les metteurs en scène. C’est exactement ainsi que la requête SQL doit être construite. On utilise deux fois la table Artiste dans la clause FROM, et on la renomme une fois en Acteur, l’autre fois en MES avec la commande SQL AS. Ensuite on utilise le nom approprié pour lever les ambiguïtés quand c’est nécessaire. SELECT Film. titre , annee , code_pays , genre , id_realisateur FROM Film , Artiste AS Acteur , Artiste AS MES , Role WHERE Film . titre LIKE ’%$ t i t r e%’ AND Acteur .nom LIKE ’%$nom_acteur%’ AND MES. nom LIKE ’%$nom_realisateur%’ AND annee BETWEEN ’$annee_min ’ AND ’$annee_max ’ AND genre LIKE ’$genre ’ AND id_acteur = Acteur.id AND id_realisateur = MES.id AND R o l e .id_film = Film. id Cette requête est d’un niveau de complexité respectable, même si on peut aller plus loin. Une manière de bien l’interpréter est de raisonner de la manière suivante. L’exécution d’une requête SQL consiste à examiner toutes les combinaisons possibles de lignes provenant de toutes les tables de la clause FROM.Onpeutalors considérer chaque nom de table dans le FROM comme une variable qui pointe sur une des lignes de la table. Dans l’exemple ci-dessus, on a donc deux variables, Acteur et MES qui pointent sur deux lignes de la table Artiste, et deux autres, Film et Role qui pointent respectivement sur des lignes des tables Film et Role. Étant données les lignes référencées par ces variables, la clause SELECT renvoie un résultat si tous les critères de la clause WHERE sont vrais simultanément. Le résultat est lui-même construit en prenant un ensemble d’attributs parmi ces lignes. . chaque nom de table dans le FROM comme une variable qui pointe sur une des lignes de la table. Dans l’exemple ci-dessus, on a donc deux variables, Acteur et MES qui pointent sur deux lignes de la. reste identique, avec une succession de clauses SELECT-FROM-WHERE, mais le FROM fait maintenant référence aux deux tables qui nous intéressent, et le critère de rapprochement de lignes venant de. dynamique provenant de la base. Les champs sont groupés par trois, et affichés dans deux tableaux en mode horizontal. private function formRecherche () { // Recherche de la liste des genres $resultat