V o i c i l e s f i l m s s é l e c t i o n n é s ( { m a x _ f i l m s } au maximum ) . Vous p o u v e z a t t r i b u e r ou c h a n g e r l e s n o t a t i o n s . < / p> < t a b l e b o r d e r = ’ 2 ’ > < t r c l a s s = ’ h e a d e r ’ >< t h> D e s c r i p t i o n du f i l m < / t h>< t h>Note< / t h>< / t r > < !−− BEGIN f i l m −−>
< !−− Champ c a c h é a v e c l ’ i d e n t i f i a n t du f i l m −−> < i n p u t t y p e = ’ hidden ’ name= " i d [ ] " v a l u e = " { i d _ f i l m } " / > < !−− A n c r e p o u r v o i r l a d e s c r i p t i o n du f i l m −−> < t d > { t i t r e } < / a> , { g e n r e } , { p a y s } , { annee } , R é a l . p a r { r e a l i s a t e u r } < / t d > < !−− Champ s e l e c t p o u r a t t r i b u e r u n e n o t e −−> { l i s t e _ n o t e s } < / td> < !−− END f i l m −−> < i n p u t t y p e = ’ s u b m i t ’ name= " v a l i d e r " v a l u e = " V a l i d e r v o s n o t a t i o n s " / > < / form>
Une ligne du tableau d’affichage est représentée par une template imbriqué nommé film. On instancie ce template autant de fois qu’on trouve de films dans le résultat de la requête basée sur les critères saisis par l’utilisateur. Voici l’action du contrôleur Notation. function recherche () { / / C o n t r ô l e de l a s e s s i o n $ t h i s −>c o n t r o l e A c c e s ( ) ; / / D é f i n i t i o n du t i t r e e t d e l a v u e $ t h i s −>vue−> t i t r e _ p a g e = " R é s u l t a t de l a r e c h e r c h e " ; $ t h i s −>vue−> s e t F i l e ( " c o n t e n u " , " n o t a t i o n _ r e c h e r c h e . t p l " ) ;
7.2 Recherche, présentation, notation des films
297
/ / E x t r a c t i o n du t e m p l a t e ’ f i l m ’ , r e m p l a c e m e n t p a r l ’ e n t i t é // ’ films ’ $ t h i s −>vue−>s e t B l o c k ( " c o n t e n u " , " f i l m " , " f i l m s " ) ; / / C r é a t i o n de l a l i s t e des n o t e s $ n o t e s = a r r a y ( " 0 " => " Non n o t é " , " 1 " => ’ ∗ ’ , " 2 " => ’ ∗∗ ’ , " 3 " => ’ ∗∗∗ ’ , " 4 " => ’ ∗∗∗∗ ’ , " 5 " => ’ ∗∗∗∗∗ ’ ) ; / / C r é a t i o n d e l a r e q u ê t e e n f o n c t i o n d e s c r i t è r e s p a s s é s au // script $ r e q u e t e = U t i l : : c r e e r R e q u e t e s ( $_POST , $ t h i s −>bd ) ; $ r e s u l t a t = $ t h i s −>bd−>e x e c R e q u e t e ( $ r e q u e t e ) ; $nb_films =1; w h i l e ( $ f i l m = $ t h i s −>bd−> o b j e t S u i v a n t ( $ r e s u l t a t ) ) { / / R e c h e r c h e du m e t t e u r e n s c è n e $mes = U t i l : : c h e r c h e A r t i s t e A v e c I D ( $ f i l m −> i d _ r e a l i s a t e u r , $ t h i s −>bd ) ; / / P l a c e m e n t d e s i n f o r m a t i o n s dans l a vue $ t h i s −>vue−> i d _ f i l m = $ f i l m −>i d ; $ t h i s −>vue−>g e n r e = $ f i l m −>g e n r e ; $ t h i s −>vue−> t i t r e = $ f i l m −> t i t r e ; $ t h i s −>vue−>annee = $ f i l m −>annee ; $ t h i s −>vue−>p a y s = $ f i l m −>c o d e _ p a y s ; $ t h i s −>vue−> r e a l i s a t e u r = $mes−>prenom . " " . $mes−>nom ; / / R e c h e r c h e de l a n o t a t i o n de l ’ u t i l i s a t e u r c o ur a nt pour // l ’ utiliser / / comme v a l e u r p a r d é f a u t d a n s l e champ d e f o r m u l a i r e d e // type l i s t e $ n o t a t i o n = U t i l : : c h e r c h e N o t a t i o n ( $ t h i s −> s e s s i o n −>e m a i l , $ f i l m −>i d , $ t h i s −>bd ) ; i f ( is_object ( $notation ) ) { $ n o t e _ d e f a u t = $ n o t a t i o n −>n o t e ; } else { $note_defaut = 0; } / / La l i s t e d e s n o t e s e s t un champ < s e l e c t > c r é é p a r u n e / / méthode s t a t i q u e de l a vue . $ t h i s −>vue−> l i s t e _ n o t e s = Te m pla t e : : c h a m p S e l e c t ( " n o t e s [ $ f i l m −>i d ] " , $ n o t e s , $note_defaut ) ; / / I n s t a n c i a t i o n du t e m p l a t e ’ f i l m ’ , a j o u t d a n s l ’ e n t i t é // ’ films ’ $ t h i s −>vue−>append ( " f i l m s " , " f i l m " ) ; i f ( $ n b _ f i l m s ++ >= s e l f : : MAX_FILMS) b r e a k ; }
298
Chapitre 7. Production du site
/ / F i n a l e m e n t on a f f i c h e l a v u e comme d ’ h a b i t u d e $ t h i s −>vue−>m a x _ f i l m s = s e l f : : MAX_FILMS ; echo $ t h i s −>vue−>r e n d e r ( " p a g e " ) ; }
On effectue une boucle classique sur le résultat de la requête. Chaque passage dans la boucle correspond à une ligne dans le tableau, avec la description du film et l’affichage d’une liste de notes. Pour cette dernière, on se retrouve face au problème classique d’engendrer un champ avec une liste d’options, qui constitue une imbrication très dense de valeurs dynamiques (les codes des options) et de textes statiques (les balises HTML). La solution adoptée ici est de s’appuyer sur une fonction utilitaire, implantée comme une méthode statique de la classe Template, qui produit le texte HTML à partir d’un tableau PHP. s t a t i c f u n c t i o n c h a m p S e l e c t ( $nom , $ l i s t e , $ d e f a u t , $ t a i l l e =1) { $options = " " ; f o r e a c h ( $ l i s t e a s $ v a l => $ l i b e l l e ) { / / A t t e n t i o n aux p r o b l è m e s d ’ a f f i c h a g e $val = htmlSpecialChars ( $val ) ; $defaut = htmlSpecialChars ( $defaut ) ; i f ( $ v a l != $ d e f a u t ) { $ o p t i o n s . = " < o p t i o n v a l u e =\" $ v a l \"> $ l i b e l l e < / o p t i o n >\n " ; } else { $ o p t i o n s . = " < o p t i o n v a l u e =\" $ v a l \" s e l e c t e d = ’1 ’ > $ l i b e l l e < / o p t i o n >\n " ; } } r e t u r n " < s e l e c t name = ’ $nom ’ s i z e = ’ $ t a i l l e ’ > " . $ o p t i o n s . " \n " ; }
Le script associé à ce formulaire reçoit donc deux tableaux PHP : d’abord$id, contenant la liste des identifiants de film ayant reçu une notation, et $notes, contenant les notes elles-mêmes. Si l’on constate que la note a changé pour un film, on exécute un UPDATE, et si la note n’existe pas on exécute un INSERT. C’est l’action noter qui se charge de cette prise en compte des notations. function noter () { $ t h i s −>c o n t r o l e A c c e s ( ) ; / / D é f i n i t i o n du t i t r e e t d e l a v u e $ t h i s −>vue−> t i t r e _ p a g e = " R é s u l t a t de l a n o t a t i o n " ; $ t h i s −>vue−> s e t F i l e ( " c o n t e n u " , " n o t a t i o n _ n o t e r . t p l " ) ; / / Boucle sur tous l e s f i l m s notés f o r e a c h ( $_POST [ ’ i d ’ ] as $id ) { $ n o t e = $_POST [ ’ n o t e s ’ ] [ $ i d ] ; $ n o t a t i o n = U t i l : : c h e r c h e N o t a t i o n ( $ t h i s −> s e s s i o n −>e m a i l ,
7.3 Affichage des films et forum de discussion
299
$ i d , $ t h i s −>bd ) ; / / On m e t à j o u r s i l a n o t e a c h a n g é i f ( ! i s _ o b j e c t ( $ n o t a t i o n ) && $ n o t e != 0 ) { $ r e q u e t e = " INSERT INTO N o t a t i o n ( i d _ f i l m , e m a i l , n o t e ) " . " VALUES ( ’ $ i d ’ , ’ { $ t h i s −> s e s s i o n −>e m a i l } ’ , ’ $ n o t e ’ ) " ; $ t h i s −>bd−>e x e c R e q u e t e ( $ r e q u e t e ) ; } e l s e i f ( i s _ o b j e c t ( $ n o t a t i o n ) && $ n o t e != $ n o t a t i o n −>n o t e ) { $ r e q u e t e = "UPDATE N o t a t i o n SET n o t e = ’ $ n o t e ’ " . " WHERE e m a i l = ’ { $ t h i s −> s e s s i o n −>e m a i l } ’ AND i d _ f i l m = ’ $id ’ " ; $ t h i s −>bd−>e x e c R e q u e t e ( $ r e q u e t e ) ; } } / / P r o d u c t i o n du f o r m u l a i r e d e r e c h e r c h e $ t h i s −>vue−> f o r m u l a i r e = $ t h i s −>f o r m R e c h e r c h e ( ) ; echo $ t h i s −>vue−>r e n d e r ( " p a g e " ) ; }
7.3 AFFICHAGE DES FILMS ET FORUM DE DISCUSSION Le contrôleur Film affiche toutes les informations connues sur un film, et propose un forum de discussion (une liste de messages permettant de le commenter). La présentation d’un film (voir l’exemple de la figure 7.4) est une mise en forme HTML des informations extraites de la base via PHP. Il s’agit d’un nouvel exemple de production d’une page par templates. function index () { / / D é f i n i t i o n du t i t r e $ t h i s −>vue−> t i t r e _ p a g e = " A f f i c h a g e d e s f i l m s " ; / / C o n t r ô l e de l a s e s s i o n $ t h i s −>c o n t r o l e A c c e s ( ) ; / / On d e v r a i t a v o i r r e ç u un i d e n t i f i a n t i f ( ! i s S e t ($_REQUEST [ ’ i d _ f i l m ’ ] ) ) { $ t h i s −>vue−>c o n t e n u = " J e ne peux p a s a f f i c h e r c e t t e p a g e : " . " i l me f a u t un i d e n t i f i a n t de f i l m " ; echo $ t h i s −>vue−>r e n d e r ( " p a g e " ) ; exit ; } / / On r e c h e r c h e l e f i l m a v e c l ’ i d $ f i l m = U t i l : : c h e r c h e F i l m ($_REQUEST [ ’ i d _ f i l m ’ ] , $ t h i s −>bd ) ;
300
Chapitre 7. Production du site
/ / S i on a t r o u v é l e f i l m , on y v a ! i f ( is_object ( $film ) ) { $ t h i s −>vue−> s e t F i l e ( " c o n t e n u " , " f i l m . t p l " ) ; / / E x t r a c t i o n du b l o c d e s a c t e u r s $ t h i s −>vue−>s e t B l o c k ( " c o n t e n u " , " a c t e u r " , " a c t e u r s " ) ; $ t h i s −>vue−> s e t B l o c k ( " c o n t e n u " , " m e s s a g e " , " m e s s a g e s " ) ; / / I l s u f f i t de p l a c e r dans l a vue l e s i n f o r m a t i o n s / / n é c e s s a i r e s à l ’ a f f i c h a g e du f i l m $ t h i s −>vue−> i d _ f i l m = $ f i l m −>i d ; $ t h i s −>vue−> t i t r e = $ f i l m −> t i t r e ; $ t h i s −>vue−>g e n r e = $ f i l m −>g e n r e ; $ t h i s −>vue−>p a y s = $ f i l m −>c o d e _ p a y s ; $ t h i s −>vue−>annee = $ f i l m −>annee ; $ t h i s −>vue−>r e s u m e = $ f i l m −>r e s u m e ; $ t h i s −>vue−> a f f i c h e = " . / a f f i c h e s / " . md5 ( $ f i l m −> t i t r e ) . " . gif " ; / / La moyenne d e s n o t e s $ t h i s −>vue−>moyenne = U t i l : : moyenneFilm ( $ f i l m −>i d , $ t h i s −> bd ) ; / / On p r e n d l e r é a l i s a t e u r $mes = U t i l : : c h e r c h e A r t i s t e A v e c I d ( $ f i l m −> i d _ r e a l i s a t e u r , $ t h i s −>bd ) ; $ t h i s −>vue−> r e a l i s a t e u r _ p r e n o m = $mes−>prenom ; $ t h i s −>vue−> r e a l i s a t e u r _ n o m = $mes−>nom ; / / Les a c t e u r s $ r e q u e t e = " SELECT nom , prenom , nom _r ole FROM A r t i s t e a , Role r " . "WHERE a . i d = r . i d _ a c t e u r AND r . i d _ f i l m = ’ $ f i l m −>i d ’ " ; $ r e s u l t a t = $ t h i s −>bd−>e x e c R e q u e t e ( $ r e q u e t e ) ; w h i l e ( $ a c t e u r = $ t h i s −>bd−> o b j e t S u i v a n t ( $ r e s u l t a t ) ) { $ t h i s −>vue−>a c t e u r _ n o m = $ a c t e u r −>nom ; $ t h i s −>vue−>a c t e u r _ p r e n o m = $ a c t e u r −>prenom ; $ t h i s −>vue−> a c t e u r _ r o l e = $ a c t e u r −>n o m _ r o l e ; $ t h i s −>vue−>append ( " a c t e u r s " , " a c t e u r " ) ; } / / Les messages sur l e f i l m $ t h i s −>vue−>m e s s a g e s = $ t h i s −> a f f i c h e M e s s ( $ f i l m −>i d , 0 ) ; echo $ t h i s −>vue−>r e n d e r ( " p a g e " ) ; } }
La seule nouveauté notable est la gestion d’un forum de discussion. Cette idée simple se rencontre couramment : il s’agit d’offrir aux internautes un moyen de déposer des commentaires ou des appréciations, sous la forme d’un message stocké
7.3 Affichage des films et forum de discussion
301
dans la base MySQL. De plus, afin de rendre possible une véritable discussion, on donne la possibilité de répondre à des messages déjà entrés.
Figure 7.4 — Présentation d’un film
Les messages constituent donc une hiérarchie, un message étant fils d’un autre s’il lui répond. La table stockant les messages doit contenir l’identifiant du film, l’e-mail de l’internaute qui a saisi le message, l’identifiant éventuel du message dont il est le fils, et bien entendu le sujet, le texte et la date de création du commentaire. Voici le script de création de la table, qui se trouve dans le fichier ComplFilms.sql . CREATE TABLE M e s s a g e ( i d INT NOT NULL, i d _ p e r e INT DEFAULT 0 , i d _ f i l m INTEGER ( 5 0 ) NOT NULL, s u j e t VARCHAR ( 3 0 ) NOT NULL, t e x t e TEXT NOT NULL, d a t e _ c r e a t i o n INT , e m a i l _ c r e a t e u r VARCHAR( 4 0 ) NOT NULL, PRIMARY KEY ( i d ) , FOREIGN KEY ( e m a i l _ c r e a t e u r ) REFERENCES Internaute ) ;
Les messages de plus haut niveau (ceux qui ne constituent pas une réponse) auront un id_pere nul, comme l’indique la clause DEFAULT. La saisie des messages s’effectue dans un formulaire produit par l’action message du contrôleur Film. Cette action s’attend à recevoir le titre du film, et éventuellement l’identifiant du message auquel on répond. Dans ce dernier cas on n’affiche pas le sujet qui reprend celui du message-père, et qui est inclus dans un champ caché. function message () { / / D é f i n i t i o n du t i t r e $ t h i s −>vue−> t i t r e _ p a g e = " A j out d ’ un m e s s a g e " ;
302
Chapitre 7. Production du site
/ / C o n t r ô l e de l a s e s s i o n $ t h i s −>c o n t r o l e A c c e s ( ) ; / / V é r i f i c a t i o n d e s v a l e u r s p a s s é e s au s c r i p t i f ( empty ($_REQUEST [ ’ i d _ f i l m ’ ] ) ) { $ t h i s −>vue−>c o n t e n u = " I l manque d e s i n f o r m a t i o n s ? ! \n " ; } else { / / Ce m e s s a g e e s t − i l l e f i l s d ’ un a u t r e m e s s a g e ? i f ( ! i s S e t ($_REQUEST [ ’ i d _ p e r e ’ ] ) ) { $id_pere = " " ; } else { $ i d _ p e r e = $_REQUEST [ ’ i d _ p e r e ’ ] ; } / / C r é a t i o n du f o r m u l a i r e $ f = new F o r m u l a i r e ( " p o s t " , " ? c t r l = f i l m& ; a c t i o n = i n s e r e r " ) ; / / Champs c a c h é s : e m a i l , t i t r e du f i l m , i d du m e s s a g e p è r e $ f −>champCache ( " e m a i l _ c r e a t e u r " , $ t h i s −> s e s s i o n −>e m a i l ) ; $ f −>champCache ( " i d _ f i l m " , $_REQUEST [ ’ i d _ f i l m ’ ] ) ; $ f −>champCache ( " i d _ p e r e " , $ i d _ p e r e ) ; / / T a b l e a u e n mode v e r t i c a l $ f −>d e b u t T a b l e ( ) ; / / S ’ i l s ’ a g i t d ’ u n e r é p o n s e : on n ’ a f f i c h e p a s l e s u j e t i f ( $ i d _ p e r e == " " o r $ i d _ p e r e == 0 ) { $ f −>champTexte ( " S u j e t " , " s u j e t " , " " , 3 0 ) ; } else { $ f −>a j o u t T e x t e ( " Réponse au m e s s a g e < i > " . $_REQUEST [ ’ s u j e t ’ ] . " \n " ) ; $ f −>champCache ( " s u j e t " , $_REQUEST [ ’ s u j e t ’ ] ) ; } $ f −>c h a m p F e n e t r e ( " M e s s a g e " , " t e x t e " , " " , 4 , 5 0 ) ; $ f −> f i n T a b l e ( ) ; $ f −>c h a m p V a l i d e r ( " E n r e g i s t r e r l e m e s s a g e " , " v a l i d e r " ) ; / / A f f i c h a g e du f o r m u l a i r e $ t h i s −>vue−> f o r m u l a i r e = $ f −>formulaireHTML ( ) ; $ t h i s −>vue−> i d _ f i l m = $_REQUEST [ ’ i d _ f i l m ’ ] ; $ t h i s −>vue−> e m a i l _ c r e a t e u r = $ t h i s −> s e s s i o n −>e m a i l ; $ t h i s −>vue−>i d _ p e r e = $ i d _ p e r e ; $ t h i s −>vue−> s e t F i l e ( " c o n t e n u " , " f i l m _ m e s s a g e . t p l " ) ; } echo $ t h i s −>vue−>r e n d e r ( " p a g e " ) ; }
7.3 Affichage des films et forum de discussion
303
Nous ne donnons pas le code d’insertion des messages similaire à ceux déjà vus pour les films ou les internautes. Vous pouvez le trouver dans le code de FilmCtrl.php. En revanche, il est plus intéressant d’examiner l’affichage des messages, qui doit se faire de manière hiérarchique, avec pour chaque message l’ensemble de ses descendants, le nombre de niveaux n’étant pas limité. Comme souvent avec ce type de structure, une fonction récursive permet de résoudre le problème de manière élégante et concise. La méthode afficheMess() est chargée d’afficher, pour un film, la liste des réponses à un message dont l’identifiant est passé en paramètre. Pour chacun de ces messages, on crée une ancre permettant de lui répondre, et, plus important, on appelle à nouveau (récursivement) la fonction AfficheMess() en lui passant l’identifiant du message courant pour afficher tous ses fils. La récursion s’arrête quand on ne trouve plus de fils. Le code présente une subtilité pour la gestion de la vue. Ce que l’on doit afficher ici, c’est un arbre dont chaque nœud correspond à un message et constitue la racine du sous-arbre correspondant à l’ensemble de ses descendants. Pour l’assemblage final avec le moteur de templates, on doit associer un nom d’entité à chaque nœud. C’est le rôle de la variable nom_groupe ci-dessous qui identifie de manière unique le nœud correspondant à un message et à ses descendants par la chaîne groupid , où id est l’identifiant du message. La fonction affichemess() renvoie la représentation HTML du nœud courant, ce qui correspond donc à une instanciation de bas vers le haut de l’ensemble des nœuds constituant l’arborescence. private function afficheMess ( $id_film , $id_pere ) { / / R e c h e r c h e d e s m e s s a g e s f i l s du p è r e c o u r a n t $ r e q u e t e = " SELECT ∗ FROM M e s s a g e " . "WHERE i d _ f i l m = ’ $ i d _ f i l m ’ AND i d _ p e r e = ’ $ i d _ p e r e ’ " ; / / On c r é e u n e e n t i t é n o m _ g r o u p e p o u r p l a c e r l a p r é s e n t a t i o n / / d e s r é p o n s e s au m e s s a g e i d _ p e r e $nom_groupe = " g r o u p e " . $ i d _ p e r e ; $ t h i s −>vue−>s e t V a r ( $nom_groupe , " " ) ; / / A f f i c h e d e s m e s s a g e s d a n s une l i s t e , a v e c a p p e l s r é c u r s i f s $ r e s u l t a t = $ t h i s −>bd−>e x e c R e q u e t e ( $ r e q u e t e ) ; w h i l e ( $ m e s s a g e = $ t h i s −>bd−> o b j e t S u i v a n t ( $ r e s u l t a t ) ) { / / Appel r é c u r s i f p o u r o b t e n i r l a m i s e en f o r m e d e s // réponses $ t h i s −>vue−>r e p o n s e s = $ t h i s −> a f f i c h e M e s s ( $ i d _ f i l m , $ m e s s a g e −>i d ) ; / / On p l a c e l e s i n f o r m a t i o n s d a n s l a v u e $ t h i s −>vue−>t e x t e _ m e s s a g e = $ m e s s a g e −> t e x t e ; $ t h i s −>vue−>i d _ p e r e = $ m e s s a g e −>i d ; $ t h i s −>vue−> s u j e t = $ m e s s a g e −> s u j e t ; / / A t t e n t i o n à b i e n c o d e r l e t e x t e p l a c é d a n s u n e URL $ t h i s −>vue−> s u j e t _ c o d e = u r l E n c o d e ( $ m e s s a g e −> s u j e t ) ; $ t h i s −>vue−> e m a i l _ c r e a t e u r = $ m e s s a g e −> e m a i l _ c r e a t e u r ;
304
Chapitre 7. Production du site
/ / D é c o d a g e d e l a d a t e Unix $ i d a t e = g e t D a t e ( $ m e s s a g e −> d a t e _ c r e a t i o n ) ; / / Mise en f o r m e de l a d a t e d é c o d $ t h i s −>vue−>d a t e _ m e s s a g e = $ i d a t e [ ’ mday ’ ] . " / " . $ i d a t e [ ’ mon ’ ] . " / " . $ i d a t e [ ’ y e a r ’ ]; $ t h i s −>vue−>h e u r e _ m e s s a g e = $ i d a t e [ ’ h o u r s ’ ] . " h e u r e s " . $idate [ ’ minutes ’ ] ; i f ( $ i d _ p e r e != 0 ) $ t h i s −>vue−> p r e f i x e = " RE : " ; else $ t h i s −>vue−> p r e f i x e = " " ; / / C r é a t i o n d e l a p r é s e n t a t i o n du m e s s a g e c o u r a n t , e t / / c o n c a t é n a t i o n dans l ’ e n t i t é $nom_groupe $ t h i s −>vue−>append ( $nom_groupe , " m e s s a g e " ) ; } / / On r e n v o i e l e s m e s s a g e s du n i v e a u c o u r a n t , a v e c t o u t e s // leurs réponses r e t u r n $ t h i s −>vue−>g e t V a r ( $nom_groupe ) ; }
Au moment de l’appel initial à cette fonction, on lui donne l’identifiant 0, ce qui revient à afficher au premier niveau tous les messages qui ne constituent pas des réponses. Ensuite, à chaque appel à afficheMess(), on ouvre une nouvelle balise |