Beruflich Dokumente
Kultur Dokumente
fonction method_exists()
Code web : 439083
52
LHYDRATATION
Elle prend en premier paramtre le nom de la classe ou une instance de cette classe, et
en deuxime paramtre le nom de la mthode qui nous intresse. La mthode renvoie
true si la mthode existe, sinon false.
Je vous laisse ajouter cette condition qui permet de voir si le setter correspondant
existe.
Voici la correction :
1 <?php
2 // ...
3 public function hydrate(array $donnees)
4 {
5 foreach ($donnees as $key => $value)
6 {
7 $method = 'set'.ucfirst($key);
8
9 if (method_exists($this , $method))
10 {
11 // ...
12 }
13 }
14 }
15 // ...
16 ?>
Et maintenant, il ne reste plus qu appeler le setter lintrieur de la condition ! Pour
cela, je vais vous apprendre une petite astuce. Il est possible dappeler une mthode dy-
namiquement, cest--dire appeler une mthode dont le nom nest pas connu lavance
(en dautres termes, le nom est connu seulement pendant lexcution et est donc stock
dans une variable). Pour ce faire, rien de plus simple ! Regardez ce code :
1 <?php
2 class A
3 {
4 public function hello ()
5 {
6 echo 'Hello world !';
7 }
8 }
9
10 $a = new A;
11 $method = 'hello';
12
13 $a ->$method (); // Affiche : Hello world !
14 ?>
Il est bien entendu possible de passer des arguments la mthode.
53
CHAPITRE 4. MANIPULATION DE DONNES STOCKES
Vous tes maintenant capables de crer la dernire instruction dans notre mthode !
Pour rappel, celle-ci doit invoquer le setter dont le nom est contenu dans la variable
$method.
1 <?php
2 // ...
3 public function hydrate(array $donnees)
4 {
5 foreach ($donnees as $key => $value)
6 {
7 // On rcupre le nom du setter correspondant l'attribut.
8 $method = 'set'.ucfirst($key);
9
10 // Si le setter correspondant existe.
11 if (method_exists($this , $method))
12 {
13 // On appelle le setter.
14 $this ->$method($value);
15 }
16 }
17 }
18 // ...
19 ?>
Cette fonction est trs importante, vous la retrouverez dans de nombreux codes (parfois
sous des formes direntes) provenant de plusieurs dveloppeurs. Gardez-l donc dans
un coin de votre tte.
Il est courant dimplmenter un constructeur ces classes demandant un
tableau de valeurs pour quil appelle ensuite la fonction dhydratation an
que lobjet soit hydrat ds sa cration, comme on la vu au dbut de ce
chapitre.
Grer sa BDD correctement
On vient de voir jusqu prsent comment grer les donnes que les requtes nous
renvoient, mais o placer ces requtes ? Notre but est de programmer orient objet, donc
nous voulons le moins de code possible en-dehors des classes pour mieux lorganiser.
Beaucoup de dbutants sont tents de placer les requtes dans des mthodes de la classe
reprsentant une entit de la BDD. Par exemple, dans le cas du personnage, je parie
que la plupart dentre vous seraient tents dcrire les mthodes add() et update()
dans la classe Personnage, ayant respectivement pour rle dexcuter une requte pour
ajouter et modier un personnage en BDD.
Arrtez-vous l tout de suite ! Je vais vous expliquer pourquoi vous faites fausse route,
puis vous montrerai la bonne marche suivre.
54
GRER SA BDD CORRECTEMENT
Une classe, un rle
En POO, il y a une phrase trs importante quil faut que vous ayez constamment en
tte : Une classe, un rle. Maintenant, rpondez clairement cette question : Quel
est le rle dune classe comme Personnage ?
Un objet instanciant une classe comme Personnage a pour rle de reprsenter une ligne
prsente en BDD. Le verbe reprsenter est ici trs important. En eet, reprsen-
ter est trs dirent de grer . Une ligne de la BDD ne peut pas sauto-grer ! Cest
comme si vous demandiez un ouvrier ayant construit un produit de le commerciali-
ser : louvrier est tout fait capable de le construire, cest son rle, mais il ne soccupe
pas du tout de sa gestion, il en est incapable. Il faut donc quune deuxime personne
intervienne, un commercial, qui va soccuper de vendre ce produit.
Pour revenir nos objets dorigine, nous aurons donc besoin de quelque chose qui va
soccuper de les grer. Ce quelque chose, vous laurez peut-tre devin, nest autre
quun objet. Un objet grant des entits issues dune BDD est gnralement appel un
manager .
Comme un manager ne fonctionne pas sans support de stockage (dans notre cas, une
BDD), on va prendre un exemple concret en crant un gestionnaire pour nos per-
sonnages, qui va donc se charger den ajouter, den modier, den supprimer et den
rcuprer. Puisque notre classe est un gestionnaire de personnages, je vous propose de
la nommer PersonnagesManager. Cependant, rappelez-vous les questions que lon doit
se poser pour tablir le plan de notre classe :
Quelles seront les caractristiques de mes objets ?
Quelles seront les fonctionnalits de mes objets ?
Les caractristiques dun manager
Partie dlicate, car cela est moins intuitif que de trouver les caractristiques dun
personnage. Pour trouver la rponse, vous devrez passer par une autre question (ce
serait trop simple de toujours rpondre la mme question !) : De quoi a besoin un
manager pour fonctionner ?
Mme si la rponse ne vous vient pas spontanment lesprit, vous la connaissez : elle
a besoin dune connexion la BDD pour pouvoir excuter des requtes. En utilisant
PDO, vous devriez savoir que la connexion la BDD est reprsente par un objet, un
objet daccs la BDD (ou DAO pour Database Access Object). Vous lavez peut-tre
compris : notre manager aura besoin de cet objet pour fonctionner, donc notre classe
aura un attribut stockant cet objet.
Notre classe a-t-elle besoin dautre chose pour fonctionner ?
Non, cest tout. Vous pouvez donc commencer crire votre classe :
1 <?php
2 class PersonnagesManager
3 {
4 private $_db;
55
CHAPITRE 4. MANIPULATION DE DONNES STOCKES
5 }
6 ?>
Les fonctionnalits dun manager
Pour pouvoir grer au mieux des entits prsentes en BDD (ici, nos personnages), il va
falloir quelques fonctionnalits de base. Quelles sont-elles ?
Un manager doit pouvoir :
enregistrer une nouvelle entit ;
modier une entit ;
supprimer une entit ;
slectionner une entit.
Cest le fameux CRUD (Create, Read, Update, Delete). Noublions pas aussi dajouter
un setter pour notre manager an de pouvoir modier lattribut $_db (pas besoin
daccesseur). La cration dun constructeur sera aussi indispensable si nous voulons
assigner cet attribut un objet PDO ds linstanciation du manager.
Nous allons donc crire notre premier manager en crivant les mthodes correspondant
ces fonctionnalits. crivez dans un premier temps les mthodes en ne les remplissant
que de commentaires dcrivant les instructions qui y seront crites. Cela vous permettra
dorganiser clairement le code dans votre tte.
1 <?php
2 class PersonnagesManager
3 {
4 private $_db; // Instance de PDO.
5
6 public function __construct($db)
7 {
8 $this ->setDb($db);
9 }
10
11 public function add(Personnage $perso)
12 {
13 // Prparation de la requte d'insertion.
14 // Assignation des valeurs pour le nom , la force , les dg
ts , l'exprience et le niveau du personnage.
15 // Excution de la requte.
16 }
17
18 public function delete(Personnage $perso)
19 {
20 // Excute une requte de type DELETE.
21 }
22
23 public function get($id)
24 {
56
GRER SA BDD CORRECTEMENT
25 // Excute une requte de type SELECT avec une clause WHERE
, et retourne un objet Personnage.
26 }
27
28 public function getList ()
29 {
30 // Retourne la liste de tous les personnages.
31 }
32
33 public function update(Personnage $perso)
34 {
35 // Prpare une requte de type UPDATE.
36 // Assignation des valeurs la requte.
37 // Excution de la requte.
38 }
39
40 public function setDb(PDO $db)
41 {
42 $this ->_db = $db;
43 }
44 }
45 ?>
Une fois cette tape accomplie, vous pouvez remplacer les commentaires par les ins-
tructions correspondantes. Cest la partie la plus facile car vous lavez dj fait de
nombreuses fois en procdural : ce ne sont que des requtes taper et des valeurs
retourner.
1 <?php
2 class PersonnagesManager
3 {
4 private $_db; // Instance de PDO
5
6 public function __construct($db)
7 {
8 $this ->setDb($db);
9 }
10
11 public function add(Personnage $perso)
12 {
13 $q = $this ->_db ->prepare('INSERT INTO personnages SET nom =
:nom , forcePerso = :forcePerso , degats = :degats ,
niveau = :niveau , experience = :experience ');
14
15 $q ->bindValue(':nom', $perso ->nom());
16 $q ->bindValue(':forcePerso ', $perso ->forcePerso (), PDO::
PARAM_INT);
17 $q ->bindValue(':degats ', $perso ->degats (), PDO:: PARAM_INT);
18 $q ->bindValue(':niveau ', $perso ->niveau (), PDO:: PARAM_INT);
19 $q ->bindValue(':experience ', $perso ->experience (), PDO::
PARAM_INT);
57
CHAPITRE 4. MANIPULATION DE DONNES STOCKES
20
21 $q->execute ();
22 }
23
24 public function delete(Personnage $perso)
25 {
26 $this ->_db ->exec('DELETE FROM personnages WHERE id = '.
$perso ->id());
27 }
28
29 public function get($id)
30 {
31 $id = (int) $id;
32
33 $q = $this ->_db ->query('SELECT id, nom , forcePerso , degats ,
niveau , experience FROM personnages WHERE id = '.$id);
34 $donnees = $q->fetch(PDO:: FETCH_ASSOC);
35
36 return new Personnage($donnees);
37 }
38
39 public function getList ()
40 {
41 $persos = array();
42
43 $q = $this ->_db ->query('SELECT id, nom , forcePerso , degats ,
niveau , experience FROM personnages ORDER BY nom');
44
45 while ($donnees = $q ->fetch(PDO:: FETCH_ASSOC))
46 {
47 $persos [] = new Personnage($donnees);
48 }
49
50 return $persos;
51 }
52
53 public function update(Personnage $perso)
54 {
55 $q = $this ->_db ->prepare('UPDATE personnages SET forcePerso
= :forcePerso , degats = :degats , niveau = :niveau ,
experience = :experience WHERE id = :id');
56
57 $q->bindValue(':forcePerso ', $perso ->forcePerso (), PDO::
PARAM_INT);
58 $q->bindValue(':degats ', $perso ->degats (), PDO:: PARAM_INT);
59 $q->bindValue(':niveau ', $perso ->niveau (), PDO:: PARAM_INT);
60 $q->bindValue(':experience ', $perso ->experience (), PDO::
PARAM_INT);
61 $q->bindValue(':id', $perso ->id(), PDO:: PARAM_INT);
62
58
GRER SA BDD CORRECTEMENT
63 $q ->execute ();
64 }
65
66 public function setDb(PDO $db)
67 {
68 $this ->_db = $db;
69 }
70 }
71 ?>
Essayons tout a !
Faisons un petit test pour sassurer que tout cela fonctionne.
Assurez-vous de bien avoir cr une table personnages dans votre BDD.
Commenons par crer un objet Personnage :
1 <?php
2 $perso = new Personnage(array(
3 'nom' => 'Victor ',
4 'forcePerso ' => 5,
5 'degats ' => 0,
6 'niveau ' => 1,
7 'experience ' => 0
8 ));
9 ?>
Maintenant, comment lajouter en BDD? Il faudra dabord crer une instance de notre
manager, en lui passant une instance de PDO.
1 <?php
2 $perso = new Personnage(array(
3 'nom' => 'Victor ',
4 'forcePerso ' => 5,
5 'degats ' => 0,
6 'niveau ' => 1,
7 'experience ' => 0
8 ));
9
10 $db = new PDO('mysql:host=localhost;dbname=tests ', 'root', '');
11 $manager = new PersonnagesManager($db);
12 ?>
Nous navons plus quune instruction entrer : celle ordonnant lajout du personnage
en BDD. Et cest tout !
59
CHAPITRE 4. MANIPULATION DE DONNES STOCKES
1 <?php
2 $perso = new Personnage(array(
3 'nom' => 'Victor ',
4 'forcePerso ' => 5,
5 'degats ' => 0,
6 'niveau ' => 1,
7 'experience ' => 0
8 ));
9
10 $db = new PDO('mysql:host=localhost;dbname=tests ', 'root', '');
11 $manager = new PersonnagesManager($db);
12
13 $manager ->add($perso);
14 ?>
En rsum
Il est maintenant temps de mettre en pratique ce que vous venez dapprendre. Cepen-
dant, avant de continuer, assurez-vous bien de tout avoir compris, sinon vous serez
perdus car vous ne comprendrez pas le code du TP!
Du ct du script PHP, chaque enregistrement de la base de donnes est reprsent
par un objet possdant une liste dattributs identique la liste des colonnes de la
table.
Il doit tre possible dhydrater chacun des objets grce une mthode hydrate qui
a pour rle dassigner les valeurs passes en paramtre aux attributs correspondant.
La communication avec la BDD se fait par le biais dun objet dirent de lobjet
reprsentant lenregistrement (une classe = un rle). Un tel objet est un manager.
Un manager peut stocker les objets en BDD, mais peut tout--fait les stocker sur
un autre support (chier XML, chier texte, etc.).
60
Chapitre 5
TP : Mini-jeu de combat
Dicult :
V
oici un petit TP qui mettra en pratique ce que lon vient de voir. Celui-ci est troi-
tement li avec le prcdent chapitre. Ainsi, je vous conseille davoir bien compris ce
dernier avant daborder ce TP, sinon vous narriverez sans doute pas au rsultat tout
seul.
En tout, vous aurez 3 TP de la sorte dont le niveau de dicult sera croissant. Puisque
celui-ci est votre premier, tout sera expliqu dans les moindres dtails.
61
CHAPITRE 5. TP : MINI-JEU DE COMBAT
Ce quon va faire
Pour information, jutilise ici PDO. Si vous ne savez toujours pas maitriser cette API,
alors allez lire le tutoriel PDO : Interface daccs aux BDD, accessible via le code web
suivant :
Tutoriel PDO
Code web : 709358
Cahier des charges
Ce que nous allons raliser est trs simple. Nous allons crer une sorte de jeu. Chaque
visiteur pourra crer un personnage (pas de mot de passe requis pour faire simple) avec
lequel il pourra frapper dautres personnages. Le personnage frapp se verra iniger un
certain degr de dgts.
Un personnage est dni selon 2 caractristiques :
Son nom (unique).
Ses dgts.
Les dgts dun personnage sont compris entre 0 et 100. Au dbut, il a bien entendu
0 de dgt. Chaque coup qui lui sera port lui fera prendre 5 points de dgts. Une
fois arriv 100 points de dgts, le personnage est mort (on le supprimera alors de la
BDD).
Notions utilises
Voici une petite liste vous indiquant les points techniques que lon va mettre en pratique
avec ce TP :
Les attributs et mthodes ;
linstanciation de la classe ;
les constantes de classe ;
et surtout, tout ce qui touche la manipulation de donnes stockes.
Cela dit, et cest je pense ce qui sera le plus dicile, ce qui sera mis en valeur ici
sera la conception dun mini-projet, cest--dire que lon travaillera surtout ici votre
capacit programmer orient objet. Cependant, ne prenez pas trop peur : nous avons
eectu une importante partie thorique lors du prcdent chapitre, et rien de nouveau
napparatra.
Vous avez donc maintenant une ide de ce qui vous attend. Cependant, ceci tant votre
premier TP concernant la POO, il est fort probable que vous ne sachiez pas par o
commencer, et cest normal ! nous allons donc raliser le TP ensemble : je vais vous
expliquer comment penser un mini-projet et vous poser les bonnes questions.
62
PREMIRE TAPE : LE PERSONNAGE
Pr-conception
Avant de nous attaquer au cur du script, nous allons rchir son organisation. De
quoi aura-t-on besoin ? Puisque nous travaillerons avec des personnages, nous aurons
besoin de les stocker pour quils puissent durer dans le temps. Lutilisation dune base
de donnes sera donc indispensable.
Le script tant simple, nous naurons quune table personnages qui aura dirents
champs. Pour les dnir, rchissez ce qui caractrise un personnage. Ainsi nous
connaissons dj 2 champs de cette table que nous avons dnis au dbut : nom et
dgts . Et bien sr, noublions pas le plus important : lidentiant du personnage !
Chaque personnage doit possder un identiant unique qui permet ainsi de le rechercher
plus rapidement (au niveau performances) quavec son nom.
Vous pouvez donc ainsi crer votre table tous seuls via PhpMyAdmin. Si vous ntes
pas srs de vous, je vous laisse le code SQL crant cette table :
1 CREATE TABLE IF NOT EXISTS personnages (
2 id smallint(5) unsigned NOT NULL AUTO_INCREMENT ,
3 nom varchar(50) COLLATE latin1_general_ci NOT NULL ,
4 degats tinyint(3) unsigned NOT NULL DEFAULT '0',
5 PRIMARY KEY (id ),
6 UNIQUE KEY nom (nom )
7 ) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=
latin1_general_ci;
Linarisation
Code web : 267969
147
CHAPITRE 8. LES MTHODES MAGIQUES
Posons le problme
Vous avez un systme de sessions sur votre site avec une classe Connexion. Cette
classe, comme son nom lindique, aura pour rle dtablir une connexion la BDD.
Vous aimeriez bien stocker lobjet cr dans une variable $_SESSION mais vous ne
savez pas comment faire.
Ben si ! On fait $_SESSION[connexion] = $objetConnexion et puis
voil !
Oui, a fonctionne, mais savez-vous vraiment ce qui se passe quand vous eectuez une
telle opration ? Ou plutt, ce qui se passe la n du script ? En fait, la n du
script, le tableau de session est linaris automatiquement. Linariser signie que
lon transforme une variable en chane de caractres selon un format bien prcis. Cette
chane de caractres pourra, quand on le souhaitera, tre transforme dans lautre sens
(cest--dire quon va restituer son tat dorigine). Pour bien comprendre ce principe,
on va linariser nous-mmes notre objet. Voici ce que nous allons faire :
Cration de lobjet ($objetConnexion = new Connexion;) ;
transformation de lobjet en chane de caractres :
$_SESSION[connexion] = serialize($objetConnexion); ;
changement de page ;
transformation de la chane de caractres en objet :
$objetConnexion = unserialize($_SESSION[connexion]);.
Des explications simposent.
Les nouveauts rencontres ici sont lapparition de deux nouvelles fonctions : serialize
et unserialize.
La premire fonction, serialize, retourne lobjet pass en paramtre sous forme de
chane de caractres. Vous vous demandez sans doutes comment on peut transformer
un objet en chane de caractres : la rponse est toute simple. Quand on y rchit,
un objet cest quoi ? Cest un ensemble dattributs, tout simplement. Les mthodes
ne sont pas stockes dans lobjet, cest la classe qui sen occupe. Notre chane de
caractres contiendra donc juste quelque chose comme : Objet MaClasse contenant
les attributs unAttribut qui vaut Hello world! , autreAttribut qui vaut Vive la
linarisation , dernierAttribut qui vaut Et un dernier pour la route ! . Ainsi,
vous pourrez conserver votre objet dans une variable sous forme de chane de caractres.
Si vous achez cette chane par un echo par exemple, vous narriverez sans doute pas
dchirer lobjet, cest normal, ce nest pas aussi simple que la chane que jai montre
titre dexemple. Cette fonction est automatiquement appele sur larray $_SESSION
la n du script, notre objet est donc automatiquement linaris la n du script.
Cest uniquement dans un but didactique que nous linarisons manuellement.
La seconde fonction, unserialize, retourne la chane de caractres passe en para-
mtre sous forme dobjet. En gros, cette fonction lit la chane de caractres, cre une
instance de la classe correspondante et assigne chaque attribut la valeur quils avaient.
148
LINARISER SES OBJETS
Ainsi, vous pourrez utiliser lobjet retourn (appel de mthodes, attributs et diverses
oprations) comme avant. Cette fonction est automatiquement appele ds le dbut
du script pour restaurer le tableau de sessions prcdemment enregistr dans le chier.
Sachez toutefois que si vous avez linaris un objet manuellement, il ne sera jamais
restaur automatiquement.
Et quel est le rapport avec tes mthodes magiques ?
En fait, les fonctions cites ci-dessus (serialize et unserialize) ne se contentent pas
de transformer le paramtre quon leur passe en autre chose : elles vrient si, dans
lobjet pass en paramtre (pour serialize), il y a une mthode __sleep, auquel cas
elle est excute. Si cest unserialize qui est appele, la fonction vrie si lobjet
obtenu comporte une mthode __wakeup, auquel cas elle est appele.
serialize et __sleep
La mthode magique __sleep est utilise pour nettoyer lobjet ou pour sauver des
attributs. Si la mthode magique __sleep nexiste pas, tous les attributs seront sau-
vs. Cette mthode doit renvoyer un tableau avec les noms des attributs sauver.
Par exemple, si vous voulez sauver $serveur et $login, la fonction devra retourner
array(serveur, login);.
Voici ce que pourrait donner notre classe Connexion :
1 <?php
2 class Connexion
3 {
4 protected $pdo , $serveur , $utilisateur , $motDePasse ,
$dataBase;
5
6 public function __construct($serveur , $utilisateur ,
$motDePasse , $dataBase)
7 {
8 $this ->serveur = $serveur;
9 $this ->utilisateur = $utilisateur;
10 $this ->motDePasse = $motDePasse;
11 $this ->dataBase = $dataBase;
12
13 $this ->connexionBDD ();
14 }
15
16 protected function connexionBDD ()
17 {
18 $this ->pdo = new PDO('mysql:host='.$this ->serveur.';dbname=
'.$this ->dataBase , $this ->utilisateur , $this ->motDePasse
);
19 }
149
CHAPITRE 8. LES MTHODES MAGIQUES
20
21 public function __sleep ()
22 {
23 // Ici sont placer des instructions excuter juste
avant la linarisation.
24 // On retourne ensuite la liste des attributs qu'on veut
sauver.
25 return array('serveur ', 'utilisateur ', 'motDePasse ', '
dataBase ');
26 }
27 }
28 ?>
Ainsi, vous pourrez faire ceci :
1 <?php
2 $connexion = new Connexion('localhost ', 'root', '', 'tests');
3
4 $_SESSION['connexion '] = serialize($connexion);
5 ?>
unserialize et __wakeup
Maintenant, nous allons simplement implmenter la fonction __wakeup. Quallons-nous
mettre dedans ?
Rien de compliqu. . . Nous allons juste appeler la mthode connexionBDD qui se char-
gera de nous connecter notre base de donnes puisque les identiants, serveur et nom
de la base ont t sauvegards et ainsi restaurs lappel de la fonction unserialize !
1 <?php
2 class Connexion
3 {
4 protected $pdo , $serveur , $utilisateur , $motDePasse ,
$dataBase;
5
6 public function __construct($serveur , $utilisateur ,
$motDePasse , $dataBase)
7 {
8 $this ->serveur = $serveur;
9 $this ->utilisateur = $utilisateur;
10 $this ->motDePasse = $motDePasse;
11 $this ->dataBase = $dataBase;
12
13 $this ->connexionBDD ();
14 }
15
16 protected function connexionBDD ()
17 {
150
LINARISER SES OBJETS
18 $this ->pdo = new PDO('mysql:host='.$this ->serveur.';dbname=
'.$this ->dataBase , $this ->utilisateur , $this ->motDePasse
);
19 }
20
21 public function __sleep ()
22 {
23 return array('serveur ', 'utilisateur ', 'motDePasse ', '
dataBase ');
24 }
25
26 public function __wakeup ()
27 {
28 $this ->connexionBDD ();
29 }
30 }
31 ?>
Pratique, hein ?
Maintenant que vous savez ce qui se passe quand vous enregistrez un objet dans une
entre de session, je vous autorise ne plus appeler serialize et unserialize.
Ainsi, ce code fonctionne parfaitement :
1 <?php
2 session_start ();
3
4 if (!isset($_SESSION['connexion ']))
5 {
6 $connexion = new Connexion('localhost ', 'root', '', 'tests');
7 $_SESSION['connexion '] = $connexion;
8
9 echo 'Actualisez la page !';
10 }
11
12 else
13 {
14 echo '<pre >';
15 var_dump($_SESSION['connexion ']); // On affiche les infos
concernant notre objet.
16 echo '</pre >';
17 }
18 ?>
Vous voyez donc, en testant ce code, que notre objet a bel et bien t sauvegard comme
il fallait, et que tous les attributs ont t sauvs. Bref, cest magique !
151
CHAPITRE 8. LES MTHODES MAGIQUES
tant donn que notre objet est restaur automatiquement lors de lappel
de session_start(), la classe correspondante doit tre dclare avant, si-
non lobjet dsrialis sera une instance de __PHP_Incomplete_Class_Name,
classe qui ne contient aucune mthode (cela produira donc un objet inutile).
Si vous avez un autoload qui chargera la classe automatiquement, il sera
appel.
Autres mthodes magiques
Voici les dernires mthodes magiques que vous navez pas vues. Je parlerai ici de
__toString, __set_state et __invoke.
__toString
La mthode magique __toString est appele lorsque lobjet est amen tre converti
en chane de caractres. Cette mthode doit retourner la chane de caractres souhaite.
Exemple :
1 <?php
2 class MaClasse
3 {
4 protected $texte;
5
6 public function __construct($texte)
7 {
8 $this ->texte = $texte;
9 }
10
11 public function __toString ()
12 {
13 return $this ->texte;
14 }
15 }
16
17 $obj = new MaClasse('Hello world !');
18
19 // Solution 1 : le cast
20
21 $texte = (string) $obj;
22 var_dump($texte); // Affiche : string(13) "Hello world !".
23
24 // Solution 2 : directement dans un echo
25 echo $obj; // Affiche : Hello world !
26 ?>
Pas mal, hein?
152
AUTRES MTHODES MAGIQUES
__set_state
La mthode magique __set_state est appele quand vous appelez la fonction var_export
en passant votre objet exporter en paramtre. Cette fonction var_export a pour rle
dexporter la variable passe en paramtre sous forme de code PHP (chane de carac-
tres). Si vous ne spciez pas de mthode __set_state dans votre classe, une erreur
fatale sera leve.
Notre mthode __set_state prend un paramtre, la liste des attributs ainsi que leur
valeur dans un tableau associatif (array(attribut => valeur)). Notre mthode
magique devra retourner lobjet exporter. Il faudra donc crer un nouvel objet et lui
assigner les valeurs quon souhaite, puis le retourner.
Ne jamais retourner $this, car cette variable nexistera pas dans cette m-
thode ! var_export reportera donc une valeur nulle.
Puisque la fonction var_export retourne du code PHP valide, on peut utiliser la fonc-
tion eval qui excute du code PHP sous forme de chane de caractres quon lui passe
en paramtre. Vous trouverez plus dinformations sur cette fonction via le code web
suivant :
Fonction eval
Code web : 236928
Par exemple, pour retourner un objet en sauvant ses attributs, on pourrait faire :
1 <?php
2 class Export
3 {
4 protected $chaine1 , $chaine2;
5
6 public function __construct($param1 , $param2)
7 {
8 $this ->chaine1 = $param1;
9 $this ->chaine2 = $param2;
10 }
11
12 public function __set_state($valeurs) // Liste des attributs
de l'objet en paramtre.
13 {
14 $obj = new Export($valeurs['chaine1 '], $valeurs['chaine2 '])
; // On cre un objet avec les attributs de l'objet que
l'on veut exporter.
15 return $obj; // on retourne l'objet cr.
16 }
17 }
18
19 $obj1 = new Export('Hello ', 'world !');
20
21 eval('$obj2 = ' . var_export ($obj1 , true) . ';'); // On cre
153
CHAPITRE 8. LES MTHODES MAGIQUES
un autre objet , celui -ci ayant les mmes attributs que l'
objet prcdent.
22
23 echo '<pre >', print_r ($obj2 , true), '</pre >';
24 ?>
Le code achera donc ceci (voir la gure 8.7).
Figure 8.7 Rsultat ach par le script
__invoke
Disponible depuis PHP 5.3
Que diriez-vous de pouvoir utiliser lobjet comme fonction ? Vous ne voyez pas ce que
je veux dire ? Je comprends.
Voici un code qui illustrera bien le tout :
1 <?php
2 $obj = new MaClasse;
3 $obj('Petit test'); // Utilisation de l'objet comme fonction.
4 ?>
Essayez ce code et. . . BAM! Une erreur fatale (cest bizarre !). Plus srieusement, pour
rsoudre ce problme, nous allons devoir utiliser la mthode magique __invoke. Elle est
appele ds quon essaye dutiliser lobjet comme fonction (comme on vient de faire).
Cette mthode comprend autant de paramtres que darguments passs la fonction.
Exemple :
1 <?php
2 class MaClasse
3 {
4 public function __invoke($argument)
154
AUTRES MTHODES MAGIQUES
5 {
6 echo $argument;
7 }
8 }
9
10 $obj = new MaClasse;
11
12 $obj(5); // Affiche 5 .
13 ?>
En rsum
Les mthodes magiques sont des mthodes qui sont appeles automatiquement lors-
quun certain vnement est dclench.
Toutes les mthodes magiques commencent par deux underscores, vitez donc dap-
peler vos mthodes suivant ce mme modle.
Les mthodes magiques dont vous vous servirez le plus souvent sont __construct,
__set, __get et __call. Les autres sont plus gadget et vous les rencontrerez
moins souvent.
155
CHAPITRE 8. LES MTHODES MAGIQUES
156
Deuxime partie
[Thorie] Techniques avances
157
Chapitre 9
Les objets en profondeur
Dicult :
Je suis sr quactuellement, vous pensez que lorsquon fait un $objet = new MaClasse;,
la variable $objet contient lobjet que lon vient de crer. Personne ne peut vous en vouloir
puisque personne ne vous a dit que ctait faux. Et bien je vous le dis maintenant : comme
nous le verrons dans ce chapitre, une telle variable ne contient pas lobjet proprement
parler ! Ceci explique ainsi quelques comportements bizarres que peut avoir PHP avec
les objets. Nous allons ainsi parler de la dernire mthode magique que je vous avais
volontairement cache.
Une fois tout ceci expliqu, nous jouerons un peu avec nos objets en les parcourant, peu
prs de la mme faon quavec des tableaux.
Comme vous le verrez, les objets rservent bien des surprises !
159
CHAPITRE 9. LES OBJETS EN PROFONDEUR
Un objet, un identiant
Je vais commencer cette partie en vous faisant une rvlation : quand vous instanciez
une classe, la variable stockant lobjet ne stocke en fait pas lobjet lui-mme, mais un
identiant qui reprsente cet objet. Cest--dire quen faisant $objet = new Classe;,
"$objet ne contient pas lobjet lui-mme, mais son identiant unique. Cest un peu
comme quand vous enregistrez des informations dans une BDD : la plupart du temps,
vous avez un champ "id" unique qui reprsente lentre. Quand vous faites une requte
SQL, vous slectionnez llment en fonction de son id. Et bien l, cest pareil : quand
vous accdez un attribut ou une mthode de lobjet, PHP regarde lidentiant
contenu dans la variable, va chercher lobjet correspondant et eectue le traitement
ncessaire. Il est trs important que vous compreniez cette ide, sinon vous allez tre
compltement perdus pour la suite du chapitre.
Nous avons donc vu que la variable $objet contenait lidentiant de lobjet quelle a
instanci. Vrions cela :
1 <?php
2 class MaClasse
3 {
4 public $attribut1;
5 public $attribut2;
6 }
7
8 $a = new MaClasse;
9
10 $b = $a; // On assigne $b l'identifiant de $a, donc $a et $b
reprsentent le mme objet.
11
12 $a->attribut1 = 'Hello';
13 echo $b->attribut1; // Affiche Hello.
14
15 $b->attribut2 = 'Salut';
16 echo $a->attribut2; // Affiche Salut.
17 ?>
Je commente plus en dtail la ligne 10 pour ceux qui sont un peu perdus. Nous avons dit
plus haut que $a ne contenait pas lobjet lui-mme mais son identiant (un identiant
dobjet). $a contient donc lidentiant reprsentant lobjet cr. Ensuite, on assigne
$b la valeur de $a. Donc quest-ce que $b vaut maintenant ? Et bien la mme chose
que $a, savoir lidentiant qui reprsente lobjet ! $a et "$b font donc rfrence
la mme instance.
Schmatiquement, on peut reprsenter le code ci-dessus comme ceci (voir la gure 9.1).
Comme vous le voyez sur limage, en ralit, il ny a quun seul objet, quun seul
identiant, mais deux variables contenant exactement le mme identiant dobjet. Tout
ceci peut sembler abstrait, donc allez votre rythme pour bien comprendre.
Maintenant que lon sait que ces variables ne contiennent pas dobjet mais un iden-
tiant dobjet, vous tes censs savoir que lorsquun objet est pass en paramtre
160
UN OBJET, UN IDENTIFIANT
Figure 9.1 Exemple de consquences des identiants dobjet
une fonction ou renvoy par une autre, on ne passe pas une copie de lobjet mais une
copie de son identiant ! Ainsi, vous ntes pas oblig de passer lobjet en rfrence, car
vous passerez une rfrence de lidentiant de lobjet. Inutile, donc.
Cependant un problme se pose. Comment faire pour copier un objet ? Comment faire
pour pouvoir copier tous ses attributs et valeurs dans un nouvel objet unique ? On
a vu quon ne pouvait pas faire un simple $objet1 = $objet2 pour arriver cela.
Comme vous vous en doutez peut-tre, cest l quintervient le clonage dobjet.
Pour cloner un objet, cest assez simple. Il faut utiliser le mot-cl clone juste avant
lobjet copier. Exemple :
1 <?php
2 $copie = clone $origine; // On copie le contenu de l'objet
$origine dans l'objet $copie.
3 ?>
Cest aussi simple que cela. Ainsi les deux objets contiennent des identiants dirents :
par consquent, si on veut modier lun deux, on peut le faire sans quaucune proprit
de lautre ne soit modie.
Il ntait pas question dune mthode magique ?
Si si, jy viens.
Lorsque vous clonez un objet, la mthode __clone de celui-ci sera appele (du moins,
si vous lavez dnie). Vous ne pouvez pas appeler cette mthode directement. Cest
la mthode __clone de lobjet cloner qui est appele, pas la mthode __clone du
nouvel objet cr.
Vous pouvez utiliser cette mthode pour modier certains attributs pour lancien objet,
161
CHAPITRE 9. LES OBJETS EN PROFONDEUR
ou alors incrmenter un compteur dinstances par exemple.
1 <?php
2 class MaClasse
3 {
4 private static $instances = 0;
5
6 public function __construct ()
7 {
8 self:: $instances ++;
9 }
10
11 public function __clone ()
12 {
13 self:: $instances ++;
14 }
15
16 public static function getInstances ()
17 {
18 return self:: $instances;
19 }
20 }
21
22 $a = new MaClasse;
23 $b = clone $a;
24
25 echo 'Nombre d\'instances de MaClasse : ', MaClasse ::
getInstances ();
26 ?>
Ce qui achera ceci (voir la gure 9.2).
Figure 9.2 Rsultat ach par le script
162
COMPARONS NOS OBJETS
Comparons nos objets
Nous allons maintenant voir comment comparer deux objets. Cest trs simple, il sut
de faire comme vous avez toujours fait en comparant des chanes de caractres ou des
nombres. Voici un exemple :
1 <?php
2 if ($objet1 == $objet2)
3 {
4 echo '$objet1 et $objet2 sont identiques !';
5 }
6 else
7 {
8 echo '$objet1 et $objet2 sont diffrents !';
9 }
10 ?>
Cette partie ne vous expliquera donc pas comment comparer des objets mais la d-
marche que PHP excute pour les comparer et les eets que ces comparaisons peuvent
produire.
Reprenons le code ci-dessus. Pour que la condition renvoie true, il faut que $objet1 et
$objet2 aient les mmes attributs et les mmes valeurs, mais galement que les deux
objets soient des instances de la mme classe. Cest--dire que mme sils ont les mmes
attributs et valeurs mais que lun est une instance de la classe A et lautre une instance
de la classe B, la condition renverra false.
Exemple :
1 <?php
2 class A
3 {
4 public $attribut1;
5 public $attribut2;
6 }
7
8 class B
9 {
10 public $attribut1;
11 public $attribut2;
12 }
13
14 $a = new A;
15 $a ->attribut1 = 'Hello';
16 $a ->attribut2 = 'Salut';
17
18 $b = new B;
19 $b ->attribut1 = 'Hello';
20 $b ->attribut2 = 'Salut';
21
22 $c = new A;
163
CHAPITRE 9. LES OBJETS EN PROFONDEUR
23 $c->attribut1 = 'Hello';
24 $c->attribut2 = 'Salut';
25
26 if ($a == $b)
27 {
28 echo '$a == $b';
29 }
30 else
31 {
32 echo '$a != $b';
33 }
34
35 echo '<br />';
36
37 if ($a == $c)
38 {
39 echo '$a == $c';
40 }
41 else
42 {
43 echo '$a != $c';
44 }
45 ?>
Si vous avez bien suivi, vous savez ce qui va sacher, savoir ceci (voir la gure 9.3).
Figure 9.3 Rsultat ach par le script
Comme on peut le voir, $a et $b ont beau avoir les mmes attributs et les mmes
valeurs, ils ne sont pas identiques car ils ne sont pas des instances de la mme classe.
Par contre, $a et $c sont bien identiques.
Parlons maintenant de loprateur === qui permet de vrier que deux objets sont
strictement identiques. Vous navez jamais entendu parler de cet oprateur ? Allez
lire ce tutoriel :
Documentation dException
Code web : 144505
1 <?php
2 class Exception
3 {
4 protected $message = 'exception inconnu '; // Message de l'
exception.
194
DES EXCEPTIONS SPCIALISES
5 protected $code = 0; // Code de l'exception dfini par l'
utilisateur.
6 protected $file; // Nom du fichier source de l'exception.
7 protected $line; // Ligne de la source de l'exception.
8
9 final function getMessage (); // Message de l'exception.
10 final function getCode (); // Code de l'exception.
11 final function getFile (); // Nom du fichier source.
12 final function getLine (); // Ligne du fichier source.
13 final function getTrace (); // Un tableau de backtrace ().
14 final function getTraceAsString (); // Chane formatte de
trace.
15
16 /* Remplacable */
17 function __construct ($message = NULL , $code = 0);
18 function __toString (); // Chane formate pour l'affichage.
19 }
20 ?>
Ainsi, nous voyons que lon a accs aux attributs protgs de la classe et quon peut
rcrire les mthodes __construct et __toString. Toutes les autres mthodes sont
nales, nous navons donc pas le droit de les rcrire.
Nous allons donc crer notre classe MonException qui, par exemple, rcrira le construc-
teur en rendant obligatoire le premier argument ainsi que la mthode __toString pour
nacher que le message derreur (cest uniquement a qui nous intresse).
1 <?php
2 class MonException extends Exception
3 {
4 public function __construct($message , $code = 0)
5 {
6 parent :: __construct($message , $code);
7 }
8
9 public function __toString ()
10 {
11 return $this ->message;
12 }
13 }
14 ?>
Maintenant, comme vous laurez peut-tre devin, nous nallons pas lancer dexception
Exception mais une exception de type MonException.
Dans notre script, nous allons dsormais attraper uniquement les exceptions MonException,
ce qui claircira le code car cest une manire de se dire que lon ne travaille, dans
le bloc try, quavec des instructions susceptibles de lancer des exceptions de type
MonException. Exemple :
1 <?php
2 class MonException extends Exception
195
CHAPITRE 11. LES EXCEPTIONS
3 {
4 public function __construct($message , $code = 0)
5 {
6 parent :: __construct($message , $code);
7 }
8
9 public function __toString ()
10 {
11 return $this ->message;
12 }
13 }
14
15 function additionner($a , $b)
16 {
17 if (! is_numeric($a) OR !is_numeric($b))
18 {
19 throw new MonException('Les deux paramtres doivent tre
des nombres '); // On lance une exception "MonException ".
20 }
21
22 return $a + $b;
23 }
24
25 try // Nous allons essayer d'effectuer les instructions situes
dans ce bloc.
26 {
27 echo additionner(12, 3), '<br />';
28 echo additionner('azerty ', 54), '<br />';
29 echo additionner(4, 8);
30 }
31
32 catch (MonException $e) // Nous allons attraper les exceptions
"MonException" s'il y en a une qui est leve.
33 {
34 echo $e; // On affiche le message d'erreur grce la mthode
__toString que l'on a crite.
35 }
36
37 echo '<br />Fin du script '; // Ce message s'affiche , a prouve
bien que le script est excut jusqu 'au bout.
38 ?>
Ainsi, nous avons attrap uniquement les exceptions de type MonException. Essayez de
lancer une exception Exception la place et vous verrez quelle ne sera pas attrape.
Si vous dcidez dattraper, dans le bloc catch, les exceptions Exception, alors toutes
les exceptions seront attrapes car elles hritent toutes de cette classe. En fait, quand
vous hritez une classe dune autre et que vous dcidez dattraper les exceptions de la
classe parente, alors celles de la classe enfant le seront aussi.
196
DES EXCEPTIONS SPCIALISES
Emboter plusieurs blocs catch
Il est possible demboter plusieurs blocs catch. En eet, vous pouvez mettre un pre-
mier bloc attrapant les exceptions MonException suivi dun deuxime attrapant les
exceptions Exception. Si vous eectuez une telle opration et quune exception est
lance, alors PHP ira dans le premier bloc pour voir si ce type dexception doit tre
attrap, si tel nest pas le cas il va dans le deuxime, etc., jusqu ce quil tombe sur
un bloc qui lattrape. Si aucun ne lattrape, alors une erreur fatale est leve. Exemple :
1 <?php
2 class MonException extends Exception
3 {
4 public function __construct($message , $code = 0)
5 {
6 parent :: __construct($message , $code);
7 }
8
9 public function __toString ()
10 {
11 return $this ->message;
12 }
13 }
14
15 function additionner($a, $b)
16 {
17 if (! is_numeric($a) OR !is_numeric($b))
18 {
19 throw new MonException('Les deux paramtres doivent tre
des nombres '); // On lance une exception "MonException ".
20 }
21
22 if (func_num_args () > 2)
23 {
24 throw new Exception('Pas plus de deux arguments ne doivent
tre passs la fonction '); // Cette fois -ci , on lance
une exception "Exception ".
25 }
26
27 return $a + $b;
28 }
29
30 try // Nous allons essayer d'effectuer les instructions situes
dans ce bloc.
31 {
32 echo additionner(12 , 3), '<br />';
33 echo additionner(15 , 54, 45), '<br />';
34 }
35
36 catch (MonException $e) // Nous allons attraper les exceptions
"MonException" s'il y en a une qui est leve.
197
CHAPITRE 11. LES EXCEPTIONS
37 {
38 echo '[MonException] : ', $e; // On affiche le message d'
erreur grce la mthode __toString que l'on a crite.
39 }
40
41 catch (Exception $e) // Si l'exception n'est toujours pas
attrape, alors nous allons essayer d'attraper l'exception "
Exception ".
42 {
43 echo '[Exception] : ', $e->getMessage (); // La mthode
__toString () nous affiche trop d'informations , nous
voulons juste le message d'erreur.
44 }
45
46 echo '<br />Fin du script '; // Ce message s'affiche , cela
prouve bien que le script est excut jusqu'au bout.
47 ?>
Cette fois-ci, aucune exception MonException nest lance, mais une exception Exception
la t. PHP va donc eectuer les oprations demandes dans le deuxime bloc catch.
Exemple concret : la classe PDOException
Vous connaissez sans doute la bibliothque PDO? Pour rappel, voici le tutoriel qui en
traite :
Tutoriel PDO
Code web : 709358
Dans ce tutoriel, il vous est donn une technique pour capturer les erreurs gnres par
PDO : comme vous le savez maintenant, ce sont des exceptions ! Et PDO a sa classe
dexception : PDOException. Celle-ci nhrite pas directement de la classe Exception
mais de RuntimeException. Cette classe na rien de plus que sa classe mre, il sagit
juste dune classe qui est instancie pour mettre une exception lors de lexcution
du script. Il existe une classe pour chaque circonstance dans laquelle lexception est
lance : vous trouverez la liste des exceptions ici :
Tlcharger addendum
Code web : 407819
Commenons par crer une classe sur laquelle nous allons travailler tout au long de cette
partie, comme Personnage ( tout hasard). Avec addendum, toutes les annotations
sont des classes hritant dune classe de base : Annotation. Si nous voulons ajouter
une annotation, Table par exemple, notre classe pour spcier quelle table un objet
Personnage correspond, alors il faudra au pralable crer une classe Table.
Pour travailler simplement, crez sur votre ordinateur un dossier annota-
tions contenant un chier index.php, un chier Personnage.class.php qui
contiendra notre classe, un chier MyAnnotations.php qui contiendra nos
annotations, et enn le dossier addendum.
1 <?php
2 class Table extends Annotation {}
toute annotation correspond une valeur, valeur spcier lors de la dclaration de
lannotation :
1 <?php
2 /**
3 * @Table (" personnages ")
4 */
5 class Personnage
6 {
7
8 }
Nous venons donc de crer une annotation basique, mais concrtement, nous navons
pas fait grand-chose. Nous allons maintenant voir comment rcuprer cette annotation,
et plus prcisment la valeur qui lui est assigne, grce addendum.
quoi servent-elles ces annotations ?
Les annotations sont surtout utilises par les frameworks, comme PHPUnit ou Zend
Framework par exemple, ou bien les ORM
1
tel que Doctrine, qui apportent ici des
informations pour le mapping des classes. Vous naurez donc peut-tre pas utiliser
1. Object Relational Mapping
233
CHAPITRE 13. LAPI DE RFLEXIVIT
les annotations dans vos scripts, mais il est important den avoir entendu parler si vous
dcidez dutiliser des frameworks ou bibliothques les utilisant.
Rcuprer une annotation
Pour rcuprer une annotation, il va dabord falloir rcuprer la classe via la biblio-
thque en crant une instance de ReflectionAnnotatedClass, comme nous lavions
fait en dbut de chapitre avec ReflectionClass :
1 <?php
2 // On commence par inclure les fichiers ncessaires.
3 require 'addendum/annotations.php';
4 require 'MyAnnotations.php';
5 require 'Personnage.class.php';
6
7 $reflectedClass = new ReflectionAnnotatedClass('Personnage ');
Maintenant que cest fait, nous allons pouvoir rcuprer lannotation grce la m-
thode getAnnotation. De manire gnrale, cette mthode retourne une instance de
Annotation. Dans notre cas, puisque nous voulons lannotation Table, ce sera une
instance de Table qui sera retourne. La valeur de lannotation est contenue dans lat-
tribut value, attribut public disponible dans toutes les classes lles de Annotation :
1 <?php
2 // On commence par inclure les fichiers ncessaires.
3 require 'addendum/annotations.php';
4 require 'MyAnnotations.php';
5 require 'Personnage.class.php';
6
7 $reflectedClass = new ReflectionAnnotatedClass('Personnage ');
8
9 echo 'La valeur de l\'annotation <strong >Table </strong > est <
strong >', $reflectedClass ->getAnnotation('Table')->value , '
</strong >';
Il est aussi possible, pour une annotation, davoir un tableau comme valeur. Pour
raliser ceci, il faut mettre la valeur de lannotation entre accolades et sparer les
valeurs du tableau par des virgules :
1 <?php
2 /**
3 * @Type({'brute ', 'guerrier ', 'magicien '})
4 */
5 class Personnage
6 {
7
8 }
Si vous rcuprez lannotation, vous obtiendrez un tableau classique :
234
UTILISER DES ANNOTATIONS
1 <?php
2 print_r($reflectedClass ->getAnnotation('Type')->value); //
Affiche le dtail du tableau.
Vous pouvez aussi spcier des cls pour les valeurs comme ceci :
1 <?php
2 /**
3 * @Type({ meilleur = 'magicien ', 'moins bon' = 'brute ', neutre
= 'guerrier '})
4 */
5 class Personnage
6 {
7
8 }
Notez la mise entre quotes de moins bon : elles sont utiles ici car un espace
est prsent. Cependant, comme vous le voyez avec meilleur et neutre, elles
ne sont pas obligatoires.
Enn, pour nir avec les tableaux, je prcise que vous pouvez en emboter tant que
vous voulez. Pour placer un tableau dans un autre, il sut douvrir une nouvelle paire
daccolades :
1 <?php
2 /**
3 * @UneAnnotation ({ uneCle = 1337 , {uneCle2 = true , uneCle3 = '
une valeur '}})
4 */
Savoir si une classe possde telle annotation
Il est possible de savoir si une classe possde telle annotation grce la mthode
hasAnnotation :
1 <?php
2 require 'addendum/annotations.php';
3 require 'MyAnnotations.php';
4 require 'Personnage.class.php';
5
6 $reflectedClass = new ReflectionAnnotatedClass('Personnage ');
7
8 $ann = 'Table';
9 if ($reflectedClass ->hasAnnotation($ann))
10 {
11 echo 'La classe possde une annotation <strong >', $ann , '</
strong > dont la valeur est <strong >', $reflectedClass ->
getAnnotation($ann)->value , '</strong ><br />';
12 }
235
CHAPITRE 13. LAPI DE RFLEXIVIT
Une annotation multiples valeurs
Il est possible pour une annotation de possder plusieurs valeurs. Chacune de ces valeurs
est stocke dans un attribut de la classe reprsentant lannotation. Par dfaut, une
annotation ne contient quun attribut ($value) qui est la valeur de lannotation.
Pour pouvoir assigner plusieurs valeurs une annotation, il va donc falloir ajouter des
attributs notre classe. Commenons par a :
1 <?php
2 class ClassInfos extends Annotation
3 {
4 public $author;
5 public $version;
6 }
Il est important ici que les attributs soient publics pour que le code extrieur
la classe puisse modier leur valeur.
Maintenant, tout se joue lors de la cration de lannotation. Pour assigner les valeurs
souhaites aux attributs, il sut dcrire ces valeurs prcdes du nom de lattribut.
Exemple :
1 <?php
2 /**
3 * @ClassInfos(author = "vyk12", version = "1.0")
4 */
5 class Personnage
6 {
7
8 }
Pour accder aux valeurs des attributs, il faut rcuprer lannotation, comme nous
lavons fait prcdemment, et rcuprer lattribut.
1 <?php
2 $classInfos = $reflectedClass ->getAnnotation('ClassInfos ');
3
4 echo $classInfos ->author;
5 echo $classInfos ->version;
Le fait que les attributs soient publics peut poser quelques problmes. En eet, de la
sorte, nous ne pouvons pas tre srs que les valeurs assignes soient correctes. Heureu-
sement, la bibliothque nous permet de pallier ce problme en rcrivant la mthode
checkConstraints() (dclare dans sa classe mre Annotation) dans notre classe re-
prsentant lannotation, appele chaque assignation de valeur, dans lordre dans lequel
sont assignes les valeurs. Vous pouvez ainsi vrier lintgrit des donnes, et lancer une
erreur si il y a un problme. Cette mthode prend un argument : la cible do provient
lannotation. Dans notre cas, lannotation vient de notre classe Personnage, donc le
236
UTILISER DES ANNOTATIONS
paramtre sera une instance de ReflectionAnnotatedClass reprsentant Personnage.
Vous verrez ensuite que cela peut tre une mthode ou un attribut.
1 <?php
2 class ClassInfos extends Annotation
3 {
4 public $author;
5 public $version;
6
7 public function checkConstraints($target)
8 {
9 if (! is_string($this ->author))
10 {
11 throw new Exception('L\'auteur doit tre une chane de
caractres');
12 }
13
14 if (! is_numeric($this ->version))
15 {
16 throw new Exception('Le numro de version doit tre un
nombre valide ');
17 }
18 }
19 }
Des annotations pour les attributs et mthodes
Jusquici nous avons ajout des annotations une classe. Il est cependant possible den
ajouter des mthodes et attributs comme nous lavons fait pour la classe :
1 <?php
2 /**
3 * @Table (" Personnages ")
4 * @ClassInfos(author = "vyk12", version = "1.0")
5 */
6 class Personnage
7 {
8 /**
9 * @AttrInfos(description = 'Contient la force du personnage ,
de 0 100 ', type = 'int ')
10 */
11 protected $force_perso;
12
13 /**
14 * @ParamInfo(name = 'destination ', description = 'La
destination du personnage ')
15 * @ParamInfo(name = 'vitesse ', description = 'La vitesse
laquelle se dplace le personnage ')
16 * @MethodInfos(description = 'Dplace le personnage un
autre endroit ', return = true , returnDescription = '
237
CHAPITRE 13. LAPI DE RFLEXIVIT
Retourne true si le personnage peut se dplacer ')
17 */
18 public function deplacer($destination , $vitesse)
19 {
20 // ...
21 }
22 }
Pour rcuprer une de ces annotations, il faut dabord rcuprer lattribut ou la m-
thode. Nous allons pour cela nous tourner vers ReflectionAnnotatedProperty et
ReflectionAnnotatedMethod. Le constructeur de ces classes attend en premier para-
mtre le nom de la classe contenant llment et, en second, le nom de lattribut ou de
la mthode. Exemple :
1 <?php
2 $reflectedAttr = new ReflectionAnnotatedProperty('Personnage ',
'force_perso ');
3 $reflectedMethod = new ReflectionAnnotatedMethod('Personnage ',
'deplacer ');
4
5 echo 'Infos concernant l\'attribut :';
6 var_dump($reflectedAttr ->getAnnotation('AttrInfos '));
7
8 echo 'Infos concernant les paramtres de la mthode :';
9 var_dump($reflectedMethod ->getAllAnnotations('ParamInfo '));
10
11 echo 'Infos concernant la mthode :';
12 var_dump($reflectedMethod ->getAnnotation('MethodInfos '));
Notez ici lutilisation de ReflectionAnnotatedMethod::getAllAnnotations(). Cette
mthode permet de rcuprer toutes les annotations dune entit correspondant au nom
donn en argument. Si aucun nom nest donn, alors toutes les annotations de lentit
seront retournes.
Contraindre une annotation une cible prcise
Grce une annotation un peu spciale, vous avez la possibilit dimposer un type de
cible pour une annotation. En eet, jusqu maintenant, nos annotations pouvaient tre
utilises aussi bien par des classes que par des attributs ou des mthodes. Dans le cas des
annotations ClassInfos, AttrInfos, MethodInfos et ParamInfos, cela prsenterait un
non-sens quelles puissent tre utilises par nimporte quel type dlment.
Pour pallier ce problme, retournons notre classe ClassInfos. Pour dire cette
annotation quelle ne peut tre utilise que sur des classes, il faut utiliser lannotation
spciale @Target :
1 <?php
2 /** @Target (" class") */
3 class ClassInfos extends Annotation
4 {
238
UTILISER DES ANNOTATIONS
5 public $author;
6 public $version;
7 }
prsent, essayez dutiliser lannotation @ClassInfos sur un attribut ou une mthode
et vous verrez quune erreur sera leve. Cette annotation peut aussi prendre pour valeur
property, method ou nesty. Ce dernier type est un peu particulier et cette partie
commenant dj devenir un peu imposante, jai dcid de ne pas en parler. Si cela
vous intresse, je ne peux que vous conseiller daller faire un tour sur la documentation
daddendum, accessible via le code web suivant :
Documentation daddendum
Code web : 148670
Je vous ai aussi volontairement cach une classe : la classe ReflectionParameter qui
vous permet dobtenir des informations sur les paramtres de vos mthodes. Je vous
laisse vous documenter ce sujet grce au code web suivant, son utilisation est trs
simple :
ReflectionParameter
Code web : 977502
En rsum
Il est possible dobtenir des informations sur ses classes, attributs et mthodes, res-
pectivement grce ReflectionClass, ReflectionProperty et ReflectionMethod.
Utiliser des annotations sur ses classes permet, grce une bibliothque telle quad-
dendum, de rcuprer dynamiquement leurs contenus.
Lutilisation dannotations dans un but de conguration dynamique est utilise par
certains frameworks ou ORM tel que Doctrine par exemple, ce qui permet dcono-
miser un chier de conguration en plaant la description des tables et des colonnes
directement dans des annotations.
239
CHAPITRE 13. LAPI DE RFLEXIVIT
240
Chapitre 14
UML : prsentation (1/2)
Dicult :
P
rogrammer en orient objet cest bien beau, mais au dbut, cest un peu dicile. On
a du mal sy retrouver, on ne sait pas trop comment lier nos classes ni comment
penser cet ensemble. LUML est justement lun des moyens pour y parvenir. LUML
(pour Unied Modeling Language, ou langage de modlisation uni en franais) est
un langage permettant de modliser nos classes et leurs interactions. Concrtement, cela
seectue par le biais dun diagramme : vous dessinerez vos classes et les lierez suivant des
conventions bien prcises. Cela vous permettra ainsi de mieux visualiser votre application
et de mieux la penser.
La prsentation de lUML ne sera volontairement pas trs approfondie, le but nest pas de
faire de vous des pros de la modlisation, mais juste de vous enseigner les bases an que
vous puissiez tre capables de modliser vous-mmes.
241
CHAPITRE 14. UML : PRSENTATION (1/2)
UML, kzako ?
LUML est un langage de modlisation objet. Il permet donc de modliser vos objets
et ainsi reprsenter votre application sous forme de diagramme. Avant daller plus
loin, attardons nous un peu sur la signication dUML : Unied Modeling Language,
langage de modlisation uni . UML nest pas un langage proprement parler,
plutt une sorte de mthodologie.
Mais quest-ce que cest concrtement ? quoi a pourra bien me servir ?
Grce UML, vous pourrez modliser toute votre application. Quand je dis toute, jen-
tends par l la plupart de votre application car PHP nest pas un langage orient objet :
le modle objet lui a t implment au cours des versions (ds la version 4). Grce
ces diagrammes, vous pourrez donc reprsenter votre application : son fonctionnement,
sa mise en route, les actions susceptibles dtre eectues par lapplication, etc. Ces
diagrammes ont t conus pour que quelquun nayant aucune connaissance en in-
formatique puisse comprendre le fonctionnement de votre application. Certes, certains
diagrammes sont rservs aux dveloppeurs car assez diciles expliquer si on ne sait
pas programmer : ce sont ces diagrammes que nous allons tudier.
Daccord, je peux modliser mon application mais en quoi cela va-t-il maider ?
Si vous avez un gros projet (et l je ne parle pas forcment de PHP mais de tout langage
implmentant la POO), il peut tre utile de le modliser an dy voir plus clair. Si vous
vous focalisez sur la question Par o commencer ? au dbut du projet, cest quil
vous faut un regard plus objectif sur le projet. Les diagrammes vous apporteront ce
regard dirent sur votre application.
LUML peut aussi tre utile quand on commence programmer OO mais quon ne sait
pas trop comment sy prendre. Par exemple, vous ne savez pas trop encore comment
programmer orient objet de faon concrte (vous avez vu comment crer un mini-jeu,
cest cool, mais un livre dor ou un systme de news, vous savez ?). Sachez donc que
lUML va vous tre dune grande aide au dbut. a vous aidera mieux penser objet
car les diagrammes reprsentent vos classes, vous vous rendrez ainsi mieux compte
de ce quil est intelligent de faire ou pas. Cependant, lUML nest pas un remde
miracle et vous ne saurez toujours pas comment vous y prendre pour raliser votre
toute premire application, mais une fois que je vous aurai montr et que vous aurez
acquis un minimum de pratique, lUML pourra vous aider.
Enn, lUML a aussi un rle de documentation. En eet, quand vous reprsenterez
votre projet sous forme de diagramme, quiconque sachant le dchirer pourra (du
moins, si vous lavez bien construit) comprendre le droulement de votre application
et ventuellement reprendre votre projet (cest toujours mieux que la phpdoc que nous
242
UML, KZAKO?
utilisions jusqu prsent pour commenter nos classes (revenez au dernier TP si vous
avez un trou de mmoire)).
Bref, que ce soient pour les gros projets personnels ou professionnels, lUML vous suivra
partout. Cependant, tout le monde ne trouve pas lUML trs utile et certains prfrent
modliser le projet dans leur tte . Vous verrez avec le temps si vous avez rellement
besoin de lUML ou si vous pouvez vous en passer, mais pour linstant, je vous conseille
de modliser (enn, ds le prochain chapitre).
Il existe plusieurs types de diagrammes. Nous, nous allons tudier les diagrammes
de classe. Ce sont des diagrammes modlisant vos classes et montrant les direntes
interactions entre elles. Un exemple ? Regardez plutt la gure 14.1.
Figure 14.1 Diagramme UML modlisant notre dernier TP
Vous aurez sans doutes reconnu notre dernier TP reprsent sur un diagramme.
Ce diagramme a lavantage dtre trs simple tudier. Cependant, il y a des cas
complexes et des conventions connatre car, par exemple, des mthodes abstraites
nont pas le mme style dcriture quune mthode nale. Nous allons tudier les bases,
petit petit, puis, la n de ce chapitre, vous serez capable danalyser compltement
un diagramme de classe.
243
CHAPITRE 14. UML : PRSENTATION (1/2)
Modliser une classe
Premire approche
Nous allons commencer en douceur par analyser une simple classe modlise. Nous
allons prendre notre classe News simplie (voir la gure 14.2).
Figure 14.2 Notre classe News modlise
Nous avons donc ici une simple classe. Commenons par analyser celle-ci.
En haut, en gras et gros, nous avons le nom de la classe (ici, News).
Ensuite, spar du nom de la classe, vient un attribut de cette classe : il sagit de id
prcd dun #, nous verrons ce que a signie juste aprs.
Enn, spare de la liste dattributs, vient la liste des mthodes de la classe. Toutes
sont prcdes dun +, qui a, comme le #, une signication.
Nous allons commencer par analyser la signication du # et des +. Je ne vous fais pas
attendre : ces signes symbolisent la porte de lattribut ou de la mthode. Voici la liste
des trois symboles :
Le signe + : llment suivi de ce signe est public.
Le signe # : llment suivi de ce signe est protg.
Le signe - : llment suivi de ce signe est priv.
Maintenant que vous savez quoi correspondent ces signes, regardez droite de lattri-
but : suivi de deux points, on peut lire int. Cela veut dire que cet attribut est de type
int, tout simplement. int est le diminutif de integer qui veut dire entier : notre
attribut est donc un nombre entier.
droite des mthodes, nous pouvons apercevoir la mme chose. Ceci indique le type
de la valeur de retour de celle-ci. Si elle ne renvoie rien, alors on dit quelle est vide (=
void). Si elle peut renvoyer plusieurs types de rsultats dirents, alors on dit quelle
est mixte (= mixed). Par exemple, une mthode __get peut renvoyer une chane de
caractres ou un entier (regardez la classe Personnage sur le premier diagramme).
Encore une dernire chose expliquer sur ce diagramme : ce quil y a entre les pa-
renthses suivant le nom des mthodes. Comme vous vous en doutez, elles contiennent
tous les attributs ainsi que leur type (entier, tableau, chane de caractres, etc.). Si un
paramtre peut tre de plusieurs types, alors, comme pour les valeurs de retour des
mthodes, on dit quil est mixte.
Si un attribut, une mthode ou un paramtre est une instance ou en renvoie
une, il ne faut pas dire que son type est object. Son type est le nom de la
classe, savoir Personnage, Brute, Magicien, Guerrier, etc.
244
MODLISER UNE CLASSE
Ensuite, il y a des conventions concernant le style dcriture des attributs, des constantes
et des mthodes.
Sans style dcriture particulier, la mthode est normale , cest--dire ni abstraite,
ni nale (comme ici). Si la mthode est en italique, cela signie quelle est abstraite.
Pour montrer quune mthode est nale, on le spcie en strotype (on place le mot
leaf entre chevrons ct de la mthode).
Si lattribut ou la mthode est soulign, cela signie que llment est statique.
Une constante est reprsente comme tant un attribut public, statique, de type
const et est crite en majuscules.
Exemple rcapitulant tout a (voir la gure 14.3).
Figure 14.3 Exemple de classe modlise
Tous ces codes (textes en gras, souligns ou en italiques) sont une norme
quil faut respecter. Tout le monde sait quune mthode en italique signie
quelle est abstraite par exemple : ne faites donc pas comme bon vous semble !
Exercices
Maintenant que vous savez tout cela, je vais vous donner quelques attributs et mthodes
et vous allez essayer de deviner quels sont ses particularits (porte, statique ou non,
abstraite, nale, ou ni lun ni lautre, les arguments et leur type. . .).
Exercice 1 : -maMethode (param1:int) : void
Correction : nous avons ici une mthode maMethode qui est abstraite (car en italique)
et statique (car souligne). Elle ne renvoie rien et accepte un argument : $param1, qui
est un entier. Sans oublier sa visibilite, symbolise par le signe -, qui est prive.
Exercice 2 : #maMethode (param1:mixed) : array
Correction : nous avons ici une mthode maMethode ni abstraite, ni nale. Elle renvoie
un tableau et accepte un argument : $param1, qui peut tre de plusieurs types. Sans
oublier sa visibilite, symbolise par le signe #, qui est protge.
Exercice 3 : +leaf maMethode() : array
Correction : cette fois-ci, la mthode est nale (signale par le mot leaf) et statique (car
souligne). Elle ne prend aucun argument et renvoie un tableau. Quant sa visibilit,
le signe + nous informe quelle est publique.
245
CHAPITRE 14. UML : PRSENTATION (1/2)
Les dirents codes (italique, soulignements, etc.) rentreront dans votre tte au l du
temps, je ne vous impose pas de tous les apprendre sur-le-champ. Rfrez-vous autant
que ncessaire cette partie en cas de petits trous de mmoire.
Maintenant que vous savez analyser un objet, il serait bien de savoir analyser les inter-
actions entre ceux-ci, non ?
Modliser les interactions
Nous attaquons la dernire partie de ce chapitre. Nous allons voir comment modliser
les interactions entre les objets que lon a modliss. Jusqu prsent, nous savions
comment les reconnatre, mais lavantage de la POO est de crer un ensemble dobjets
qui interagissent entre eux an de crer une vritable application.
Lhritage
Parmi les interactions, on peut citer lhritage. Comme montr dans le premier dia-
gramme que vous avez vu, lhritage est symbolis par une simple che, comme indiqu
la gure 14.4.
Figure 14.4 Exemple de modlisation dune classe lle et dune classe mre
Ce diagramme quivaut au code suivant :
1 <?php
2 class Mere
3 {
4 protected $attribut1;
5 protected $attribut2;
6
7 public function methode ()
8 {
9
10 }
11 }
12
13 class Fille extends Mere
14 {
15 protected $attribut3;
16
17 public function __set($nom , $valeur)
18 {
19
246
MODLISER LES INTERACTIONS
20 }
21 }
22 ?>
Les interfaces
Vous connaissez galement les interactions avec les interfaces. En eet, comme je lai
dj dit, une interface nest rien dautre quune classe entirement abstraite, elle est
donc considre comme tel. Si une classe doit implmenter une interface, alors on
utilisera la che en pointills, comme la gure 14.5.
Figure 14.5 Exemple de modlisation dune classe implmentant une interface
Traduit en PHP, a donne :
1 <?php
2 interface iMaClasse
3 {
4 public function methode1 ();
5 public function methode2 ();
6 }
7
8 class MaClasse implements iMaClasse
9 {
10 protected $attribut;
11
12 public function methode ()
13 {
14
15 }
16
17 // Ne pas oublier d'implmenter les mthodes de l'interface !
18
19 public function methode1 ()
20 {
21
22 }
23
24 public function methode2 ()
25 {
26
27 }
28 }
29 ?>
247
CHAPITRE 14. UML : PRSENTATION (1/2)
Lassociation
Nous allons voir encore trois interactions qui se ressemblent. La premire est lasso-
ciation. On dit que deux classes sont associes lorsquune instance des deux classes est
amene interagir avec lautre instance. Lassociation entre deux classes est modlise
comme la gure 14.6.
Figure 14.6 Exemple de modlisation dune association
Lassociation est ici caractrise par le fait quune mthode de la classe NewsManager
entre en relation avec une instance de la classe News.
Attends deux secondes. . . Je vois des choses crites sur la ligne ainsi quaux
extrmits, quest-ce que cest ?
Le mot crit au centre, au-dessus de la ligne est la dnition de la relation. Il est suivi
dun petit symbole indiquant le sens de lassociation. Ainsi, on peut lire facilement
NewsManager gre News .
Ensuite, vous voyez le chire 1 crit gauche et une astrisque droite. Ce sont
les cardinalits. Ces cardinalits prsentent le nombre dinstances qui participent
linteraction. Nous voyons donc quil y a 1 manager pour une innit de news. On peut
dsormais lire facilement 1 NewsManager gre une innit de News . Les cardinalits
peuvent tre crites sous direntes formes :
x (nombre entier) : tout simplement la valeur exacte de x.
x..y : de x y (exemple : 1..5).
* : une innit.
x..* : x ou plus (exemple : 5..*).
Lagrgation
La deuxime che que je vais vous prsenter est lagrgation. Il sagit dune forme
dassociation un peu particulire : on parlera dagrgation entre deux classes lorsque
lune dentre elles contiendra au moins une instance de lautre classe. Une agrgation
est caractrise de la sorte (voir gure 14.7).
Figure 14.7 Exemple de modlisation dune agrgation
248
MODLISER LES INTERACTIONS
Vous pouvez remarquer quil ny a pas de cardinalit du ct du losange. En eet, le
ct ayant le losange signie quil y a obligatoirement une et une seule instance de la
classe par relation (ici la classe est NewsCollection).
La composition
La dernire interaction que je vais vous prsenter est la composition. La composition
est une agrgation particulire. Imaginons que nous avons une classe A qui contient
une ou plusieurs instance(s) de B. On parlera de composition si, lorsque linstance de A
sera supprime, toutes les instances de B contenues dans linstance de A sont elles aussi
supprimes (ce qui ntait pas le cas avec lagrgation). Une composition est reprsente
de la sorte (voir la gure 14.8).
Figure 14.8 Exemple de modlisation dune composition
Notez la prsence de la mthode __destruct() qui sera charge de dtruire
les instances de la classe News. Cependant, celle-ci nest pas obligatoire : vous
pouvez trs bien, dans lune de vos mthodes, placer un $this->liste[] =
new News;, et linstance cre puis stocke dans lattribut $liste sera donc
automatiquement dtruit.
Ce chapitre ne se veut pas complet pour la simple et bonne raison quil serait beaucoup
trop long de faire le tour de lUML. En eet, un cours complet sur lUML stalerait
sur beaucoup de chapitres, et ce nest clairement pas le but du tutoriel. Si ce sujet
vous intresse, je peux vous renvoyer sur le site dUML, o vous trouverez toutes les
informations ncessaires :
Cours UML
Code web : 974030
En rsum
LUML est un moyen parmi dautres de modliser son application an de mieux sy
retrouver.
La modlisation dune classe et des interactions se ralise en respectant certaines
conventions.
Il y a association lorsquune classe A se sert dune classe B.
Une agrgation est une association particulire : il y a agrgation entre A et B si
lobjet A possde une ou plusieurs instances de B.
249
CHAPITRE 14. UML : PRSENTATION (1/2)
Une composition est une agrgation particulire : il y a composition entre A et B si
toutes les instances de B contenues dans A sont supprimes lorsque A est supprime.
250
Chapitre 15
UML : modlisons nos classes (2/2)
Dicult :
V
oici la suite directe du prcdent chapitre ayant pour objectif de vous introduire
lUML. Nous savons actuellement analyser des diagrammes de classes et les interac-
tions entre elles, mais que diriez-vous de crer vous-mme ces diagrammes ?
Pour ce faire, nous allons avoir besoin dun programme : je vais vous prsenter Dia.
251
CHAPITRE 15. UML : MODLISONS NOS CLASSES (2/2)
Ayons les bons outils
Avant de se lancer tte baisse dans la modlisation, il serait bien davoir les logiciels qui
le permettront (a pourrait aider, sait-on jamais). Pour cela, nous allons avoir besoin
dun logiciel de modlisation de diagrammes UML. Il en existe plusieurs, pour ma part,
jai choisi Dia.
La raison de ce choix est simple : une extension qui permet de convertir les diagrammes
UML en code PHP a t dveloppe pour ce logiciel ! Ainsi, en quelques clics, toutes vos
classes contenant les attributs et mthodes ainsi que les interactions seront gnres,
le tout comment avec une phpdoc. Bref, cela nous simpliera grandement la tche !
Installation
Des installateurs sont disponibles sur le site suivant :
Installer Dia
Code web : 132920
Le lien de tlchargement pour Windows est sur la page daccueil. Deux petits liens
situs en-dessous de ce lien de tlchargement mnent aux pages proposant les liens
pour Linux et Mac OS X.
Installation de lextension uml2php5
Vous avez correctement install Dia sur votre ordinateur. Il faut maintenant installer
lextension uml2php5 qui nous permettra de gnrer automatiquement le code de nos
classes partir de nos diagrammes. Pour cela, il vous faut tlcharger cinq chiers.
Ces chiers sont disponibles sur le site de lextension, en cliquant sur Download
gauche. Tlchargez larchive .zip ou .tar.gz suivant vos prfrences. Dcompressez
cette archive et copiez / collez les cinq chiers dans le dossier xslt prsent dans le
rpertoire dinstallation de Dia.
Installer uml2php5
Code web : 481115
Lancer Dia
Histoire que nous soyons tous au point, je vais vous demander de lancer votre logiciel
an que nous soyons bien srs davoir tous le mme (voir la gure 15.1). Si linterface
est dirente, veillez bien avoir tlcharg la version 0.97 du logiciel (actuellement la
dernire).
252
AYONS LES BONS OUTILS
Figure 15.1 Fentre principale de Dia
253
CHAPITRE 15. UML : MODLISONS NOS CLASSES (2/2)
Deux zones principales
Linterface tant assez intuitive, je ne vais pas mtendre sur sa prsentation. Je vais
me contenter de vous donner le rle des deux parties de cette interface.
La premire partie, gauche, contient tous les outils (crer une classe, une interaction,
ou bien un trait, un cercle, etc.). La seconde, droite, est beaucoup plus grande : elle
contiendra notre diagramme. Cest lintrieur delle que lon eectuera nos oprations
pour tout mettre en uvre.
Unir les fentres
Il se peut que vous ayez deux fentres spares. Si cest le cas, cest que vous navez
pas pass largument integrated au programme. Vous pouvez passer cet argument
en lanant le logiciel en ligne de commande. Il y a cependant plus pratique, voici donc
des petites astuces suivant votre systme dexploitation.
Sous Windows
Sous Windows, vous pouvez crer un raccourci. Pour cela, allez dans le dossier dins-
tallation de Dia puis dans le dossier bin. Faites un clic droit sur le chier diaw.exe
et cliquez sur Copier. Allez dans le rpertoire o vous voulez crer le raccourci, faites
un clic droit dans ce rpertoire puis choisissez loption Coller le raccourci. Faites un
clic droit sur le chier cr, puis cliquez sur Proprits. Dans le champ texte Cible,
rajoutez la n (en dehors des guillemets) integrated. Ainsi, quand vous lancerez
Dia depuis ce raccourci, les deux fentres seront fusionnes.
Par dfaut, le raccourci prsent dans le menu dmarrer lance Dia avec loption
intergrated. Si ce nest pas le cas, modiez la cible de celui-ci comme on
vient de le faire (Clic droit > Proprits > . . .).
Sous Linux
Je vais ici vous donner une manipulation simple sous Ubuntu an de modier la cible
du lien pointant sur le logiciel dans le menu Applications. Dans le menu Systme,
allez dans Prfrences puis Menu principal. gauche de la fentre qui est apparue,
sous le menu Applications, cliquez sur Graphisme. Au milieu de la fentre, cliquez
sur diteur de diagrammes Dia. droite, cliquez sur Proprits. Dans le champ
texte Commande, placez-y dia integrated %F. Fermez le tout, relancez votre
programme via le menu et vous avez vos deux fentres fusionnes.
Sous les autres distributions, lide est la mme : il faut vous arranger pour modier le
raccourci an dajouter loption integrated.
254
MODLISER UNE CLASSE
Sous Mac OS
Hlas sous Mac OS, vous ne pouvez crer de raccourci. La solution consiste donc
lancer le logiciel en ligne de commande. Heureusement, celle-ci est assez courte et
simple retenir. En eet, un simple dia integrated sut.
Cependant, vous pouvez tlcharger un lanceur qui vous facilitera la tche pour lancer
lapplication :
Rsultat obtenir
Code web : 954276
292
CE QUE NOUS ALLONS FAIRE
Retour sur le traitement des rsultats
Je vais vous demander dessayer de vous rappeler le dernier TP, celui sur les per-
sonnages, et, plus prcisment, la manire dont nous rcuprions les rsultats. Nous
obtenions quelque chose comme a :
1 <?php
2 // On admet que $db est une instance de PDO
3
4 $q = $db ->prepare('SELECT id, nom , degats FROM personnages
WHERE nom <> :nom ORDER BY nom');
5 $q ->execute(array(':nom' => $nom));
6
7 while ($donnees = $q ->fetch(PDO:: FETCH_ASSOC))
8 {
9 $persos [] = new Personnage($donnees);
10 }
Comme vous pouvez le constater, on rcupre la liste des personnages sous forme
de tableau grce la constante PDO::FETCH_ASSOC, puis on instancie notre classe
Personnage en passant ce tableau au constructeur. Je vais vous dvoiler un moyen
plus simple et davantage optimis pour eectuer cette opration. je ne lai pas abord
prcdemment car je trouvais quil tait un peu tt pour vous en parler ds la premire
partie.
Avec PDO (comme avec MySQLi, mais nous le verrons plus tard), il existe une constante
qui signie retourne-moi le rsultat dans un objet (au mme titre que la constante
PDO::FETCH_ASSOC signie retourne-moi le rsultat dans un tableau ). Cette constante
est tout simplement PDO::FETCH_CLASS. Comment sutilise-t-elle ? Comme vous vous
en doutez peut-tre, si nous voulons que PDO nous retourne les rsultats sous forme
dinstances de notre classe News, il va falloir lui dire ! Or, nous ne pouvons pas lui dire le
nom de notre classe directement dans la mthode fetch() comme nous le faisons avec
PDO::FETCH_ASSOC. Nous allons nous servir dune autre mthode, setFetchMode() :
setFetchMode()
Code web : 728845
Si vous avez lu les toutes premires lignes de la page pointe par le code web que jai
donn, vous devriez savoir utiliser cette mthode :
1 <?php
2 // On admet que $db est une instance de PDO
3
4 $q = $db ->prepare('SELECT id, nom , degats FROM personnages
WHERE nom <> :nom ORDER BY nom');
5 $q ->execute(array(':nom' => $nom));
6
7 $q ->setFetchMode(PDO:: FETCH_CLASS , 'Personnage ');
8
9 $persos = $q ->fetchAll ();
293
CHAPITRE 17. TP : UN SYSTME DE NEWS
Notez quil nest plus utile de parcourir chaque rsultat pour les stocker dans
un tableau puisque nous navons aucune opration eectuer. Un simple
appel fetchAll() sut donc amplement.
Comme vous le voyez, puisque nous avons dit PDO la ligne 7 que nous voulions une
instance de Personnage en guise de rsultat, il nest pas utile de spcier de nouveau
quoi que ce soit lorsquon appelle la mthode fetchAll().
Vous pouvez essayer ce code et vous verrez que a fonctionne. Maintenant, je vais vous
demander deectuer un petit test. Achez la valeur des attributs dans le constructeur
de News (avec des simples echo, nallez pas chercher bien loin). Lancez de nouveau le
script. Sur votre cran vont alors sacher les valeurs que PDO a assignes notre
objet. Rien ne vous titille ? Dois-je vous rappeler que, normalement, le constructeur
dune classe est appel en premier et quil a pour rle principal dinitialiser lobjet ?
Pensez-vous que ce rle est accompli alors que les valeurs ont t assignes avant
mme que le constructeur soit appel ? Si vous aviez un constructeur qui devait
assigner des valeurs par dfaut aux attributs, celui-ci aurait cras les valeurs assignes
par PDO.
Pour rsumer, mmorisez quavec cette technique, lobjet nest pas instanci comme il
se doit. Lobjet est cr (sans que le constructeur soit appel), puis les valeurs
sont assignes aux attributs par PDO, enn le constructeur est appel.
Pour tout remettre dans lordre, il y a une autre constante : PDO::FETCH_PROPS_LATE.
Elle va de paire avec PDO::FETCH_CLASS. Essayez ds prsent cette modication :
1 <?php
2 // On admet que $db est une instance de PDO
3
4 $q = $db ->prepare('SELECT id, nom , degats FROM personnages
WHERE nom <> :nom ORDER BY nom');
5 $q->execute(array(':nom' => $nom));
6
7 $q->setFetchMode(PDO:: FETCH_CLASS | PDO:: FETCH_PROPS_LATE , '
Personnage ');
8
9 $persos = $q ->fetchAll ();
Achez avec des echo la valeur de quelques attributs dans le constructeur et vous
verrez quils auront tous une valeur nulle, pour la simple et bonne raison que cette
fois-ci, le constructeur a t appel avant que les valeurs soient assignes aux attributs
par PDO.
Maintenant, puisque le cahier des charges vous demande de rendre ce script compatible
avec lAPI MySQLi, je vais vous dire comment procder. Cest beaucoup plus simple :
au lieu dappeler la mthode fetch_assoc() sur le rsultat, appelez tout simplement
la mthode fetch_object(NomDeLaClasse).
294
CORRECTION
Mes attributs sont privs ou protgs, pourquoi PDO et MySQLi peuvent-ils
modier leur valeur sans appeler les setters ?
Procder de cette faon, cest de la bidouille de bas niveau. PDO et MySQLi sont
des API compiles avec PHP, elles ne fonctionnent donc pas comme les classes que vous
dveloppez. En plus de vous dire des btises, cela serait un hors sujet de vous expliquer
le pourquoi du comment, mais sachez juste que les API compiles ont quelques super
pouvoirs de ce genre.
Correction
Diagramme UML
Avant de donner une correction du code, je vais corriger la construction des classes en
vous donnant le diagramme UML reprsentant le module (voir la gure 17.1).
Figure 17.1 Diagramme modlisant le systme de news
Le code du systme
Pour des raisons dorganisation, jai dcid de placer les quatre classes dans un dossier
lib.
1 <?php
2 /**
3 * Classe reprsentant une news , cre l'occasion d'un TP du
tutoriel La programmation oriente objet en PHP
disponible sur http ://www.siteduzero.com/
295
CHAPITRE 17. TP : UN SYSTME DE NEWS
4 * @author Victor T.
5 * @version 2.0
6 */
7 class News
8 {
9 protected $erreurs = array (),
10 $id ,
11 $auteur ,
12 $titre ,
13 $contenu ,
14 $dateAjout ,
15 $dateModif;
16
17 /**
18 * Constantes relatives aux erreurs possibles rencontres
lors de l'excution de la mthode.
19 */
20 const AUTEUR_INVALIDE = 1;
21 const TITRE_INVALIDE = 2;
22 const CONTENU_INVALIDE = 3;
23
24
25 /**
26 * Constructeur de la classe qui assigne les donnes spcifi
es en paramtre aux attributs correspondants.
27 * @param $valeurs array Les valeurs assigner
28 * @return void
29 */
30 public function __construct($valeurs = array())
31 {
32 if (!empty($valeurs)) // Si on a spcifi des valeurs ,
alors on hydrate l'objet.
33 {
34 $this ->hydrate($valeurs);
35 }
36 }
37
38 /**
39 * Mthode assignant les valeurs spcifies aux attributs
correspondant.
40 * @param $donnees array Les donnes assigner
41 * @return void
42 */
43 public function hydrate($donnees)
44 {
45 foreach ($donnees as $attribut => $valeur)
46 {
47 $methode = 'set'.ucfirst($attribut);
48
49 if (is_callable(array($this , $methode)))
296
CORRECTION
50 {
51 $this ->$methode($valeur);
52 }
53 }
54 }
55
56 /**
57 * Mthode permettant de savoir si la news est nouvelle.
58 * @return bool
59 */
60 public function isNew ()
61 {
62 return empty($this ->id);
63 }
64
65 /**
66 * Mthode permettant de savoir si la news est valide.
67 * @return bool
68 */
69 public function isValid ()
70 {
71 return !(empty($this ->auteur) || empty($this ->titre) ||
empty($this ->contenu));
72 }
73
74
75 // SETTERS //
76
77 public function setId($id)
78 {
79 $this ->id = (int) $id;
80 }
81
82 public function setAuteur($auteur)
83 {
84 if (! is_string($auteur) || empty($auteur))
85 {
86 $this ->erreurs [] = self:: AUTEUR_INVALIDE;
87 }
88 else
89 {
90 $this ->auteur = $auteur;
91 }
92 }
93
94 public function setTitre($titre)
95 {
96 if (! is_string($titre) || empty($titre))
97 {
98 $this ->erreurs [] = self:: TITRE_INVALIDE;
297
CHAPITRE 17. TP : UN SYSTME DE NEWS
99 }
100 else
101 {
102 $this ->titre = $titre;
103 }
104 }
105
106 public function setContenu($contenu)
107 {
108 if (! is_string($contenu) || empty($contenu))
109 {
110 $this ->erreurs [] = self:: CONTENU_INVALIDE;
111 }
112 else
113 {
114 $this ->contenu = $contenu;
115 }
116 }
117
118 public function setDateAjout($dateAjout)
119 {
120 if (is_string($dateAjout) && preg_match('le [0-9]{2}/[0-9
]{2}/[0-9]{4} [0-9]{2}h[0-9]{2}', $dateAjout))
121 {
122 $this ->dateAjout = $dateAjout;
123 }
124 }
125
126 public function setDateModif($dateModif)
127 {
128 if (is_string($dateModif) && preg_match('le [0-9]{2}/[0-9
]{2}/[0-9]{4} [0-9]{2}h[0-9]{2}', $dateModif))
129 {
130 $this ->dateModif = $dateModif;
131 }
132 }
133
134 // GETTERS //
135
136 public function erreurs ()
137 {
138 return $this ->erreurs;
139 }
140
141 public function id()
142 {
143 return $this ->id;
144 }
145
146 public function auteur ()
298
CORRECTION
147 {
148 return $this ->auteur;
149 }
150
151 public function titre ()
152 {
153 return $this ->titre;
154 }
155
156 public function contenu ()
157 {
158 return $this ->contenu;
159 }
160
161 public function dateAjout ()
162 {
163 return $this ->dateAjout;
164 }
165
166 public function dateModif ()
167 {
168 return $this ->dateModif;
169 }
170 }
1 <?php
2 abstract class NewsManager
3 {
4 /**
5 * Mthode permettant d'ajouter une news.
6 * @param $news News La news ajouter
7 * @return void
8 */
9 abstract protected function add(News $news);
10
11 /**
12 * Mthode renvoyant le nombre de news total.
13 * @return int
14 */
15 abstract public function count ();
16
17 /**
18 * Mthode permettant de supprimer une news.
19 * @param $id int L'identifiant de la news supprimer
20 * @return void
21 */
22 abstract public function delete($id);
23
24 /**
25 * Mthode retournant une liste de news demande.
299
CHAPITRE 17. TP : UN SYSTME DE NEWS
26 * @param $debut int La premire news slectionner
27 * @param $limite int Le nombre de news slectionner
28 * @return array La liste des news. Chaque entre est une
instance de News.
29 */
30 abstract public function getList($debut = -1, $limite = -1);
31
32 /**
33 * Mthode retournant une news prcise.
34 * @param $id int L'identifiant de la news rcuprer
35 * @return News La news demande
36 */
37 abstract public function getUnique($id);
38
39 /**
40 * Mthode permettant d'enregistrer une news.
41 * @param $news News la news enregistrer
42 * @see self::add()
43 * @see self:: modify ()
44 * @return void
45 */
46 public function save(News $news)
47 {
48 if ($news ->isValid ())
49 {
50 $news ->isNew() ? $this ->add($news) : $this ->update($news)
;
51 }
52 else
53 {
54 throw new RuntimeException('La news doit tre valide pour
tre enregistre');
55 }
56 }
57
58 /**
59 * Mthode permettant de modifier une news.
60 * @param $news news la news modifier
61 * @return void
62 */
63 abstract protected function update(News $news);
64 }
1 <?php
2 class NewsManager_PDO extends NewsManager
3 {
4 /**
5 * Attribut contenant l'instance reprsentant la BDD.
6 * @type PDO
7 */
300
CORRECTION
8 protected $db;
9
10 /**
11 * Constructeur tant charg d'enregistrer l'instance de PDO
dans l'attribut $db.
12 * @param $db PDO Le DAO
13 * @return void
14 */
15 public function __construct(PDO $db)
16 {
17 $this ->db = $db;
18 }
19
20 /**
21 * @see NewsManager ::add()
22 */
23 protected function add(News $news)
24 {
25 $requete = $this ->db->prepare('INSERT INTO news SET auteur
= :auteur , titre = :titre , contenu = :contenu , dateAjout
= NOW(), dateModif = NOW()');
26
27 $requete ->bindValue(':titre', $news ->titre());
28 $requete ->bindValue(':auteur ', $news ->auteur ());
29 $requete ->bindValue(':contenu ', $news ->contenu ());
30
31 $requete ->execute ();
32 }
33
34 /**
35 * @see NewsManager :: count()
36 */
37 public function count ()
38 {
39 return $this ->db ->query('SELECT COUNT (*) FROM news')->
fetchColumn ();
40 }
41
42 /**
43 * @see NewsManager :: delete ()
44 */
45 public function delete($id)
46 {
47 $this ->db->exec('DELETE FROM news WHERE id = '.(int) $id);
48 }
49
50 /**
51 * @see NewsManager :: getList ()
52 */
53 public function getList($debut = -1, $limite = -1)
301
CHAPITRE 17. TP : UN SYSTME DE NEWS
54 {
55 $sql = 'SELECT id , auteur , titre , contenu , DATE_FORMAT (
dateAjout , \'le %d/%m/%Y %Hh%i\') AS dateAjout ,
DATE_FORMAT (dateModif , \'le %d/%m/%Y %Hh%i\') AS
dateModif FROM news ORDER BY id DESC';
56
57 // On vrifie l'intgrit des paramtres fournis.
58 if ($debut != -1 || $limite != -1)
59 {
60 $sql .= ' LIMIT '.(int) $limite.' OFFSET '.(int) $debut;
61 }
62
63 $requete = $this ->db->query($sql);
64 $requete ->setFetchMode(PDO:: FETCH_CLASS | PDO::
FETCH_PROPS_LATE , 'News');
65
66 $listeNews = $requete ->fetchAll ();
67
68 $requete ->closeCursor ();
69
70 return $listeNews;
71 }
72
73 /**
74 * @see NewsManager :: getUnique ()
75 */
76 public function getUnique($id)
77 {
78 $requete = $this ->db->prepare('SELECT id, auteur , titre ,
contenu , DATE_FORMAT (dateAjout , \'le %d/%m/%Y %Hh%i
\') AS dateAjout , DATE_FORMAT (dateModif , \'le %d/%m/%Y
%Hh%i\') AS dateModif FROM news WHERE id = :id');
79 $requete ->bindValue(':id', (int) $id , PDO:: PARAM_INT);
80 $requete ->execute ();
81
82 $requete ->setFetchMode(PDO:: FETCH_CLASS | PDO::
FETCH_PROPS_LATE , 'News');
83
84 return $requete ->fetch ();
85 }
86
87 /**
88 * @see NewsManager :: update ()
89 */
90 protected function update(News $news)
91 {
92 $requete = $this ->db->prepare('UPDATE news SET auteur = :
auteur , titre = :titre , contenu = :contenu , dateModif =
NOW() WHERE id = :id');
93
302
CORRECTION
94 $requete ->bindValue(':titre', $news ->titre());
95 $requete ->bindValue(':auteur ', $news ->auteur ());
96 $requete ->bindValue(':contenu ', $news ->contenu ());
97 $requete ->bindValue(':id', $news ->id(), PDO:: PARAM_INT);
98
99 $requete ->execute ();
100 }
101 }
1 <?php
2 class NewsManager_MySQLi extends NewsManager
3 {
4 /**
5 * Attribut contenant l'instance reprsentant la BDD.
6 * @type MySQLi
7 */
8 protected $db;
9
10 /**
11 * Constructeur tant charg d'enregistrer l'instance de
MySQLi dans l'attribut $db.
12 * @param $db MySQLi Le DAO
13 * @return void
14 */
15 public function __construct(MySQLi $db)
16 {
17 $this ->db = $db;
18 }
19
20 /**
21 * @see NewsManager ::add()
22 */
23 protected function add(News $news)
24 {
25 $requete = $this ->db->prepare('INSERT INTO news SET auteur
= ?, titre = ?, contenu = ?, dateAjout = NOW(),
dateModif = NOW()');
26
27 $requete ->bind_param('sss', $news ->auteur (), $news ->titre()
, $news ->contenu ());
28
29 $requete ->execute ();
30 }
31
32 /**
33 * @see NewsManager :: count()
34 */
35 public function count ()
36 {
37 return $this ->db ->query('SELECT id FROM news')->num_rows;
303
CHAPITRE 17. TP : UN SYSTME DE NEWS
38 }
39
40 /**
41 * @see NewsManager :: delete ()
42 */
43 public function delete($id)
44 {
45 $id = (int) $id;
46
47 $requete = $this ->db->prepare('DELETE FROM news WHERE id =
?');
48
49 $requete ->bind_param('i', $id);
50
51 $requete ->execute ();
52 }
53
54 /**
55 * @see NewsManager :: getList ()
56 */
57 public function getList($debut = -1, $limite = -1)
58 {
59 $listeNews = array ();
60
61 $sql = 'SELECT id , auteur , titre , contenu , DATE_FORMAT (
dateAjout , \'le %d/%m/%Y %Hh%i\') AS dateAjout ,
DATE_FORMAT (dateModif , \'le %d/%m/%Y %Hh%i\') AS
dateModif FROM news ORDER BY id DESC';
62
63 // On vrifie l'intgrit des paramtres fournis.
64 if ($debut != -1 || $limite != -1)
65 {
66 $sql .= ' LIMIT '.(int) $limite.' OFFSET '.(int) $debut;
67 }
68
69 $requete = $this ->db->query($sql);
70
71 while ($news = $requete ->fetch_object('News'))
72 {
73 $listeNews [] = $news;
74 }
75
76 return $listeNews;
77 }
78
79 /**
80 * @see NewsManager :: getUnique ()
81 */
82 public function getUnique($id)
83 {
304
CORRECTION
84 $id = (int) $id;
85
86 $requete = $this ->db->prepare('SELECT id, auteur , titre ,
contenu , DATE_FORMAT (dateAjout , \'le %d/%m/%Y %Hh%i
\') AS dateAjout , DATE_FORMAT (dateModif , \'le %d/%m/%Y
%Hh%i\') AS dateModif FROM news WHERE id = ?');
87 $requete ->bind_param('i', $id);
88 $requete ->execute ();
89
90 $requete ->bind_result($id , $auteur , $titre , $contenu ,
$dateAjout , $dateModif);
91
92 $requete ->fetch();
93
94 return new News(array(
95 'id' => $id ,
96 'auteur ' => $auteur ,
97 'titre' => $titre ,
98 'contenu ' => $contenu ,
99 'dateAjout ' => $dateAjout ,
100 'dateModif ' => $dateModif
101 ));
102 }
103
104 /**
105 * @see NewsManager :: update ()
106 */
107 protected function update(News $news)
108 {
109 $requete = $this ->db->prepare('UPDATE news SET auteur = ?,
titre = ?, contenu = ?, dateModif = NOW() WHERE id = ?')
;
110
111 $requete ->bind_param('sssi', $news ->auteur (), $news ->titre
(), $news ->contenu (), $news ->id());
112
113 $requete ->execute ();
114 }
115 }
Pour accder aux instances de PDO et MySQLi, nous allons nous aider du design
pattern factory. Veuillez donc crer une simple classe DBFactory.
1 <?php
2 class DBFactory
3 {
4 public static function getMysqlConnexionWithPDO ()
5 {
6 $db = new PDO('mysql:host=localhost;dbname=news', 'root', '
');
305
CHAPITRE 17. TP : UN SYSTME DE NEWS
7 $db ->setAttribute(PDO:: ATTR_ERRMODE , PDO:: ERRMODE_EXCEPTION
);
8
9 return $db;
10 }
11
12 public static function getMysqlConnexionWithMySQLi ()
13 {
14 return new MySQLi('localhost ', 'root', '', 'news');
15 }
16 }
Nous allons crer deux pages : index.php qui sera accessible au grand public et lis-
tera les news, ainsi que admin.php qui nous permettra de grer les news. Dans ces
deux pages, nous aurons besoin dun autoload. Nous allons donc crer un chier auto-
load.inc.php dans le dossier lib qui contiendra notre autoload. Il sagit dun simple
chier, voyez par vous-mme :
1 <?php
2 function autoload($classname)
3 {
4 if (file_exists($file = dirname (__FILE__) . '/' . $classname
. '.class.php'))
5 {
6 require $file;
7 }
8 }
9
10 spl_autoload_register('autoload ');
Maintenant que nous avons cr la partie interne, nous allons nous occuper des pages
qui sacheront devant vos yeux. Il sagit bien entendu de la partie la plus facile, le
pire est derrire nous.
Commenons par la page dadministration :
1 <?php
2 require 'lib/autoload.inc.php';
3
4 $db = DBFactory :: getMysqlConnexionWithMySQLi ();
5 $manager = new NewsManager_MySQLi($db);
6
7 if (isset($_GET['modifier ']))
8 {
9 $news = $manager ->getUnique ((int) $_GET['modifier ']);
10 }
11
12 if (isset($_GET['supprimer ']))
13 {
14 $manager ->delete ((int) $_GET['supprimer ']);
15 $message = 'La news a bien t supprime !';
306
CORRECTION
16 }
17
18 if (isset($_POST['auteur ']))
19 {
20 $news = new News(
21 array(
22 'auteur ' => $_POST['auteur '],
23 'titre' => $_POST['titre'],
24 'contenu ' => $_POST['contenu ']
25 )
26 );
27
28 if (isset($_POST['id']))
29 {
30 $news ->setId($_POST['id']);
31 }
32
33 if ($news ->isValid ())
34 {
35 $manager ->save($news);
36
37 $message = $news ->isNew() ? 'La news a bien t ajoute !'
: 'La news a bien t modifie !';
38 }
39 else
40 {
41 $erreurs = $news ->erreurs ();
42 }
43 }
44 ?>
45 <!DOCTYPE html PUBLIC " -//W3C//DTD XHTML 1.0 Strict //EN" "http
:// www.w3.org/TR/xhtml1/DTD/xhtml1 -strict.dtd">
46 <html xmlns="http :// www.w3.org/1999/xhtml" xml:lang="fr">
47 <head >
48 <title >Administration </title >
49 <meta http -equiv="Content -type" content="text/html; charset
=iso -8859 -1" />
50
51 <style type="text/css">
52 table , td {
53 border: 1px solid black;
54 }
55
56 table {
57 margin:auto;
58 text -align: center;
59 border -collapse: collapse;
60 }
61
62 td {
307
CHAPITRE 17. TP : UN SYSTME DE NEWS
63 padding: 3px;
64 }
65 </style >
66 </head >
67
68 <body >
69 <p><a href=".">Accder l'accueil du site </a></p>
70
71 <form action =" admin.php" method ="post">
72 <p style ="text -align: center">
73 <?php
74 if (isset($message))
75 {
76 echo $message , '<br />';
77 }
78 ?>
79 <?php if (isset($erreurs) && in_array(News::
AUTEUR_INVALIDE , $erreurs)) echo 'L\'auteur est
invalide.<br />'; ?>
80 Auteur : <input type="text" name="auteur" value="<?php
if (isset($news)) echo $news ->auteur (); ?>" /><br />
81
82 <?php if (isset($erreurs) && in_array(News::
TITRE_INVALIDE , $erreurs)) echo 'Le titre est
invalide.<br />'; ?>
83 Titre : <input type="text" name="titre" value=" <?php if
(isset($news)) echo $news ->titre (); ?>" /><br />
84
85 <?php if (isset($erreurs) && in_array(News::
CONTENU_INVALIDE , $erreurs)) echo 'Le contenu est
invalide.<br />'; ?>
86 Contenu :<br /><textarea rows="8" cols="60" name="
contenu" ><?php if (isset($news)) echo $news ->contenu
(); ?></textarea ><br />
87 <?php
88 if(isset($news) && !$news ->isNew())
89 {
90 ?>
91 <input type="hidden" name="id" value=" <?php echo $news
->id(); ?>" />
92 <input type="submit" value="Modifier" name="modifier"
/>
93 <?php
94 }
95 else
96 {
97 ?>
98 <input type="submit" value="Ajouter" />
99 <?php
100 }
308
CORRECTION
101 ?>
102 </p>
103 </form >
104
105 <p style="text -align: center">Il y a actuellement <?php
echo $manager ->count (); ?> news. En voici la liste :</p>
106
107 <table >
108 <tr><th>Auteur </th ><th>Titre </th ><th >Date d'ajout </th><th
>Dernire modification </th ><th>Action </th ></tr >
109 <?php
110 foreach ($manager ->getList () as $news)
111 {
112 echo '<tr><td>', $news ->auteur (), '</td><td>', $news ->titre()
, '</td><td>', $news ->dateAjout (), '</td><td>', ($news ->
dateAjout () == $news ->dateModif () ? '-' : $news ->dateModif
()), '</td><td><a href="?modifier=', $news ->id(), '">
Modifier </a> | <a href="?supprimer=', $news ->id(), '">
Supprimer </a></td ></tr>', "\n";
113 }
114 ?>
115 </table >
116 </body >
117 </html >
Et enn, la partie visible tous vos visiteurs :
1 <?php
2 require 'lib/autoload.inc.php';
3
4 $db = DBFactory :: getMysqlConnexionWithMySQLi ();
5 $manager = new NewsManager_MySQLi($db);
6 ?>
7 <!DOCTYPE html PUBLIC " -//W3C//DTD XHTML 1.0 Strict //EN" "http
:// www.w3.org/TR/xhtml1/DTD/xhtml1 -strict.dtd">
8 <html xmlns="http :// www.w3.org/1999/xhtml" xml:lang="fr">
9 <head >
10 <title >Accueil du site </title >
11 <meta http -equiv="Content -type" content="text/html; charset
=iso -8859 -1" />
12 </head >
13
14 <body >
15 <p><a href="admin.php">Accder l'espace d'administration
</a></p>
16 <?php
17 if (isset($_GET['id']))
18 {
19 $news = $manager ->getUnique ((int) $_GET['id']);
20
309
CHAPITRE 17. TP : UN SYSTME DE NEWS
21 echo '<p>Par <em>', $news ->auteur (), '</em>, ', $news ->
dateAjout (), '</p>', "\n",
22 '<h2>', $news ->titre(), '</h2>', "\n",
23 '<p>', nl2br($news ->contenu ()), '</p>', "\n";
24
25 if ($news ->dateAjout () != $news ->dateModif ())
26 {
27 echo '<p style ="text -align: right;"><small ><em>Modifie ',
$news ->dateModif (), '</em ></small ></p>';
28 }
29 }
30
31 else
32 {
33 echo '<h2 style="text -align:center">Liste des 5 dernires
news </h2>';
34
35 foreach ($manager ->getList(0, 5) as $news)
36 {
37 if (strlen($news ->contenu ()) <= 200)
38 {
39 $contenu = $news ->contenu ();
40 }
41
42 else
43 {
44 $debut = substr($news ->contenu (), 0, 200);
45 $debut = substr($debut , 0, strrpos($debut , ' ')) . '...';
46
47 $contenu = $debut;
48 }
49
50 echo '<h4><a href ="?id=', $news ->id(), '">', $news ->titre()
, '</a></h4>', "\n",
51 '<p>', nl2br($contenu), '</p>';
52 }
53 }
54 ?>
55 </body >
56 </html >
310
Troisime partie
[Pratique] Ralisation dun site
web
311
Chapitre 18
Description de lapplication
Dicult :
I
l serait temps de faire un TP consquent pour mettre en pratique tout ce que nous
avons vu : nous allons raliser un site web (souvent compar une application). Ce
sera loccasion de faire un point sur vos connaissances en orient objet.
Pour y arriver, nous allons crer une bibliothque, un module de news avec commentaires
ainsi quun espace dadministration complet. Le plus dicile consiste dvelopper notre
bibliothque.
Ceci est une tape importante quil ne faut surtout pas ngliger. Ne lisez donc pas ce
chapitre trop rapidement, sinon vous risquez dtre perdus par la suite. Par ailleurs, ne vous
dcouragez pas si vous ne comprenez pas tout dun coup : ce chapitre est dun niveau plus
lev , il est donc normal de ne pas tout comprendre ds la premire lecture !
313
CHAPITRE 18. DESCRIPTION DE LAPPLICATION
Une application, quest ce que cest ?
Le droulement dune application
Avant de commencer crer une application, encore faudrait-il savoir ce que cest et
en quoi cela consiste. En fait, il faut dcomposer le droulement des actions eectues
du dbut la n (le dbut tant la requte envoye par le client et la n tant la
rponse renvoye ce client). De manire trs schmatique, voici le droulement dune
application (voir la gure 18.1).
Figure 18.1 Schma simpli du droulement dune application
Dtaillons ce schma, tape par tape.
Lancement de lapplication : lorsque linternaute accdera votre site, un chier
PHP est excut sur le serveur. Dans notre cas, ce chier sera excut chaque
fois que le visiteur voudra accder une page, quelle que soit cette dernire.
Que le visiteur veuille acher une news, ouvrir un forum ou poster un commentaire,
cest ce chier qui sera excut (nous verrons comment plus tard). Ce chier sera trs
rduit : il ne fera que lancer lapplication (nous verrons plus tard quil se contente
dinstancier une classe et dinvoquer une mthode).
Chargement de la requte du client : cette tape consiste analyser la requte
envoye par le client. Cest lors de cette tape que lapplication ira chercher les
variables transmises par formulaire ou par URL (les fameuses variables GET et
POST).
Excution de la page dsire : cest ici le cur de lexcution de lapplication.
Mais comment lapplication connat-elle la page excuter ? Quelle action le visiteur
veut-il excuter ? Veut-il acher une news, visiter un forum, poster un commentaire ?
Cette action est dtermine par ce quon appelle un routeur. En analysant lURL, le
routeur est capable de savoir ce que le visiteur veut. Par exemple, si lURL entre par
le visiteur est http ://www.monsupersite.com/news-12.html, alors le routeur saura
que le visiteur veut acher la news ayant pour identiant 12 dans la base de donnes.
Le routeur va donc retourner cette action lapplication qui lexcutera (nous verrons
plus tard ce dont il sagit vraiment).
314
UNE APPLICATION, QUEST CE QUE CEST?
Envoi de la rponse au client : aprs avoir excut laction dsire (par exemple,
si le visiteur veut acher une news, laction correspondante est de rcuprer cette
news), lapplication va acher le tout, et lexcution du script sera termine. Cest
ce moment-l que la page sera envoye au visiteur.
Pour bien cerner le principe, prenons un exemple. Si le visiteur veut voir la news n12,
alors il demandera la page news-12.html. Schmatiquement, voici ce qui va se passer
(voir la gure 18.2).
Figure 18.2 Dtails des oprations eectues lorsquune page est demande
Il est trs important de comprendre ce principe. Si vous ne saisissez pas tout, nhsitez
pas relire les explications, sinon vous risquez de ne pas suivre la suite du cours.
Vous tes maintenant au courant quune application sexcute et quelle a pour rle
315
CHAPITRE 18. DESCRIPTION DE LAPPLICATION
dorchestrer lexcution du script an de donner la page au visiteur. Dans un tutoriel
sur la POO en PHP, je suis sr que vous savez comment sera reprsent cette application
dans votre script. . . Oui, notre application sera un objet ! Qui dit objet dit classe, et
qui dit classe dit fonctionnalits. Reprenez le schma et noncez-moi les fonctionnalits
que possde notre classe Application. . .
Pour linstant, elle ne possde quune fonctionnalit : celle de sexcuter (nous verrons
par la suite quelle en possdera plusieurs autres, mais vous ne pouviez pas les deviner).
Cette fonctionnalit est obligatoire quelle que soit lapplication : quel serait lintrt
dune application si elle ne pouvait pas sexcuter ?
En gnral, dans un site web, il y a deux applications : le frontend et le backend. La
premire est lapplication accessible par tout le monde : cest travers cette application
quun visiteur pourra acher une news, lire un sujet dun forum, poster un commen-
taire, etc. La seconde application est lespace dadministration : laccs est bloqu aux
visiteurs. Pour y accder, une paire identiant-mot de passe est requise. Comme vous
laurez peut-tre compris, ces deux applications seront reprsentes par deux classes
hritant de notre classe de base Application.
Un peu dorganisation
Nous avons vu susamment de notions pour se pencher sur larchitecture de notre
projet. En eet, nous savons que notre site web sera compos de deux applications
(frontend et backend) ainsi que dune classe de base Application.
La classe Application fait partie de notre bibliothque (ou library en anglais),
comme de nombreuses autres classes dont nous parlerons plus tard. Toutes ces classes
vont, par consquent, tre places dans un dossier /Library.
Pour les deux applications, cest un peu plus compliqu. En eet, une application ne
tient pas dans un seul chier : elle est divise en plusieurs parties. Parmi ces parties,
nous trouvons la plus grosse : la partie contenant les modules.
Quest-ce quun module ?
Un module est un ensemble dactions et de donnes concernant une partie du site. Par
exemple, les actions acher une news et commenter une news font partie du
mme module de news, tandis que les actions acher ce sujet et poster dans ce
sujet font partie du module du forum.
Ainsi, je vous propose larchitecture suivante :
Le dossier /Applications contiendra nos applications (frontend et backend).
Le sous-dossier /Applications/Nomdelapplication/Modules contiendra les mo-
dules de lapplication (par exemple, si lapplication frontend possde un module de
news, alors il y aura un dossier /Applications/Frontend/Modules/News).
Ne vous inquitez pas, nous reviendrons en dtail sur ces fameux modules. Jai introduit
316
UNE APPLICATION, QUEST CE QUE CEST?
la notion ici pour parler de larchitecture (et surtout de ce qui va suivre). En eet, vous
devriez vous posez une question : quand lutilisateur veut acher une page, quel chier
PHP sera excut en premier ? Ou pourrai-je placer mes feuilles de style et mes images ?
Tous les chiers accessibles au public devront tre placs dans un dossier /Web. Pour
tre plus prcis, votre serveur HTTP ne pointera pas vers la racine du projet, mais vers
le dossier /Web. Je mexplique.
Sur votre serveur local, si vous tapez localhost dans la barre dadresse, alors votre
serveur renverra le contenu du dossier C :\Wamp\www si vous tes sous Wamp-
Server, ou du dossier /var/www si vous tes sous LAMP. Vous tes-vous dj de-
mand comment est-ce que cela se faisait ? Qui a dcrt cela ? En fait, cest crit
quelque part, dans un chier de conguration. Il y en a un qui dit que si on tape
localhost dans la barre dadresse, alors on se connectera sur lordinateur. Ce chier
de conguration est le chier hosts. Le chemin menant ce chier est C :\win-
dows\system32\drivers\etc\hosts sous Windows et /etc/hosts sous Linux et
Mac OS. Vous pouvez lditer ( condition davoir les droits). Faites le test : ajou-
tez la ligne suivante la n du chier.
1 127.0.0.1 monsupersite
Sauvegardez le chier et fermez-le. Ouvrez votre navigateur, tapez monsupersite et. . .
vous atterrissez sur la mme page que quand vous tapiez localhost !
Maintenant, nous allons utiliser un autre chier. La manipulation va consister dire
lordinateur que lorsquon tape monsupersite, on ne voudra pas le contenu de
C :\Wamp\www ou de /var/www, mais de C :\Wamp\www\monsupersite\Web
si vous tes sous Windows ou de /var/www/monsupersite/Web si vous tes sous
Linux ou Mac OS.
Tout dabord, je vous annonce que nous allons utiliser le module mod_vhost_alias
dApache. Assurez-vous donc quil soit bien activ. Le chier que nous allons mani-
puler est le chier de conguration dApache, savoir httpd.conf sous WampServer.
Rajoutez la n du chier :
1 <VirtualHost\index{VirtualHost} *:80 >
2 ServerAdmin webmaster@localhost
3
4 # Mettez ici le nom de domaine que vous avez utilis dans le
fichier hosts.
5 ServerName monsupersite
6
7 # Mettez ici le chemin vers lequel doit pointer le domaine.
8 # Je suis sous Linux. Si vous tes sous Windows , le chemin
sera de la forme C:\Wamp\www\monsupersite\Web
9 DocumentRoot /home/victor/www/monsupersite/Web
10 <Directory /home/victor/www/monsupersite/Web >
11 Options Indexes FollowSymLinks MultiViews
12
13 # Cette directive permet d'activer les .htaccess.
14 AllowOverride All
15
317
CHAPITRE 18. DESCRIPTION DE LAPPLICATION
16 # Si le serveur est accessible via l'Internet mais que vous
n'en faites qu'une utilisation personnelle
17 # pensez interdire l'accs tout le monde
18 # sauf au localhost , sinon vous ne pourrez pas y accder !
19 deny from all
20 allow from localhost
21 </Directory >
22 </VirtualHost >
Si vous avez un serveur LAMP, nessayez pas de trouver le chier httpd.conf, il nexiste
pas !
La cration dhtes virtuels seectue en crant un nouveau chier contenant la congu-
ration de ce dernier. Le chier crer est /etc/apache2/sites-available/monsupersite
(remplacez monsupersite par le domaine choisi). Placez-y lintrieur le contenu que
je vous ai donn. Ensuite, il ny a plus qu activer cette nouvelle conguration grce
la commande sudo a2ensite monsupersite (remplacez monsupersite par le nom
du chier contenant la conguration de lhte virtuel).
Dans tous les cas, que vous soyez sous Windows, Linux, Mac OS ou quoi que ce soit
dautre, redmarrez Apache pour que la nouvelle conguration soit prise en compte.
Essayez dentrer monsupersite dans la barre dadresse, et vous verrez que le naviga-
teur vous ache le dossier spci dans la conguration dApache !
Les entrailles de lapplication
Avant daller plus loin, il est indispensable (voire obligatoire) de savoir utiliser le pattern
MVC
1
. Pour cela, je vous conseille daller lire le tutoriel suivant :
fonctions de tamporisation de
sortie
Code web : 313485
Documentation extract
Code web : 779902
1 <?php
2 namespace Library;
3
4 class Page extends ApplicationComponent
5 {
6 protected $contentFile;
7 protected $vars = array();
8
9 public function addVar($var , $value)
10 {
11 if (! is_string($var) || is_numeric($var) || empty($var))
12 {
13 throw new \InvalidArgumentException('Le nom de la
variable doit tre une chaine de caractre non nulle')
;
14 }
15
16 $this ->vars[$var] = $value;
17 }
18
19 public function getGeneratedPage ()
20 {
21 if (! file_exists($this ->contentFile))
22 {
23 throw new \RuntimeException('La vue spcifie n\'existe
pas');
24 }
25
26 extract($this ->vars);
27
28 ob_start ();
29 require $this ->contentFile;
30 $content = ob_get_clean ();
31
32 ob_start ();
350
LA PAGE
33 require __DIR__.'/../ Applications/'.$this ->app ->name().'/
Templates/layout.php';
34 return ob_get_clean ();
35 }
36
37 public function setContentFile($contentFile)
38 {
39 if (! is_string($contentFile) || empty($contentFile))
40 {
41 throw new \InvalidArgumentException('La vue spcifie est
invalide ');
42 }
43
44 $this ->contentFile = $contentFile;
45 }
46 }
Retour sur la classe BackController
Maintenant que nous avons crit notre classe Page, je peux vous faire crire une instruc-
tion dans la classe BackController et, plus particulirement, la mthode setView($view).
En eet, lorsque lon change de vue, il faut en informer la page concerne grce la
mthode setContentFile() de notre classe Page :
1 <?php
2 namespace Library;
3
4 abstract class BackController extends ApplicationComponent
5 {
6 // ...
7
8 public function setView($view)
9 {
10 if (! is_string($view) || empty($view))
11 {
12 throw new \InvalidArgumentException('La vue doit tre une
chaine de caractres valide ');
13 }
14
15 $this ->view = $view;
16
17 $this ->page ->setContentFile(__DIR__.'/../ Applications/'.
$this ->app ->name().'/Modules/'.$this ->module.'/Views/'.
$this ->view.'.php');
18 }
19 }
351
CHAPITRE 19. DVELOPPEMENT DE LA BIBLIOTHQUE
Retour sur la mthode HTTPResponse::redirect404()
tant donn que vous avez compris comment fonctionne un objet Page, vous tes
capables dcrire cette mthode laisse vide jusqu prsent. Comment procder ?
On commence dabord par crer une instance de la classe Page que lon stocke dans
lattribut correspondant.
On assigne ensuite le chier qui fait oce de vue gnrer la page. Ce chier
contient le message derreur format. Vous pouvez placer tous ces chiers dans le
dossier /Errors par exemple, sous le nom code.html. Le chemin menant au chier
contenant lerreur 404 sera donc /Errors/404.html.
On ajoute un header disant que le document est non trouv (HTTP/1.0 404 Not
Found).
On envoie la rponse.
Et voici ce que lon obtient :
1 <?php
2 namespace Library;
3
4 class HTTPResponse extends ApplicationComponent
5 {
6 // ...
7
8 public function redirect404 ()
9 {
10 $this ->page = new Page($this ->app);
11 $this ->page ->setContentFile(__DIR__.'/../ Errors/404.html');
12
13 $this ->addHeader('HTTP/1.0 404 Not Found');
14
15 $this ->send();
16 }
17
18 // ...
19 }
Bonus : lutilisateur
Cette classe est un bonus , cest--dire quelle nest pas indispensable lapplication.
Cependant, nous allons nous en servir plus tard donc ne sautez pas cette partie ! Mais
rassurez-vous, nous aurons vite fait de la crer.
Rchissons, schmatisons
Lutilisateur, quest-ce que cest ? Lutilisateur est celui qui visite votre site. Comme
tout site web qui se respecte, nous avons besoin denregistrer temporairement lutilisa-
352
BONUS : LUTILISATEUR
teur dans la mmoire du serveur an de stocker des informations le concernant. Nous
crons donc une session pour lutilisateur. Vous connaissez sans doute ce systme de
sessions avec le tableau $_SESSION et les fonctions ce sujet que propose lAPI. Notre
classe, que nous nommerons User, devra nous permettre de grer facilement la session
de lutilisateur. Nous pourrons donc, par le biais dun objet User :
Assigner un attribut lutilisateur.
Obtenir la valeur dun attribut.
Authentier lutilisateur (cela nous sera utile lorsque nous ferons un formulaire de
connexion pour lespace dadministration).
Savoir si lutilisateur est authenti.
Assigner un message informatif lutilisateur que lon achera sur la page.
Savoir si lutilisateur a un tel message.
Et enn, rcuprer ce message.
Cela donne naissance une classe de ce genre (voir la gure 19.10).
Figure 19.10 Modlisation de la classe User
Codons
Avant de commencer coder la classe, il faut que vous ajoutiez linstruction invoquant
session_start() au dbut du chier, en dehors de la classe. Ainsi, ds linclusion du
chier par lautoload, la session dmarrera et lobjet cr sera fonctionnel.
Ceci tant, voici le code que je vous propose :
1 <?php
2 namespace Library;
3
4 session_start ();
5
6 class User extends ApplicationComponent
7 {
8 public function getAttribute($attr)
9 {
353
CHAPITRE 19. DVELOPPEMENT DE LA BIBLIOTHQUE
10 return isset($_SESSION[$attr ]) ? $_SESSION[$attr] : null;
11 }
12
13 public function getFlash ()
14 {
15 $flash = $_SESSION['flash'];
16 unset($_SESSION['flash']);
17
18 return $flash;
19 }
20
21 public function hasFlash ()
22 {
23 return isset($_SESSION['flash']);
24 }
25
26 public function isAuthenticated ()
27 {
28 return isset($_SESSION['auth']) && $_SESSION['auth'] ===
true;
29 }
30
31 public function setAttribute($attr , $value)
32 {
33 $_SESSION[$attr] = $value;
34 }
35
36 public function setAuthenticated($authenticated = true)
37 {
38 if (! is_bool($authenticated))
39 {
40 throw new \InvalidArgumentException('La valeur spcifie
la mthode User:: setAuthenticated () doit tre un
boolean ');
41 }
42
43 $_SESSION['auth'] = $authenticated;
44 }
45
46 public function setFlash($value)
47 {
48 $_SESSION['flash'] = $value;
49 }
50 }
Comme promis, ce fut court, et tout ce qui compose cette classe est, il me semble,
facilement comprhensible.
354
BONUS 2 : LA CONFIGURATION
Pensez modier votre classe Application an dajouter un attribut $user
et crer lobjet User dans le constructeur que vous stockerez dans lattribut
cr.
Bonus 2 : la conguration
Cette classe est galement un bonus dans la mesure o elle nest pas essentielle pour
que lapplication fonctionne. Je vous encourage vous entraner crer cette classe :
tout comme la classe User, celle-ci nest pas complique (si tant est que vous sachiez
parser du XML avec une bibliothque telle que DOMDocument).
Rchissons, schmatisons
Tout site web bien conu se doit dtre congurable souhait. Par consquent, il faut
que chaque application possde un chier de conguration dclarant des paramtres
propres ladite application. Par exemple, si nous voulons acher un nombre de news
prcis sur laccueil, il serait prfrable de spcier un paramtre nombre_de_news
lapplication que nous mettrons par exemple cinq plutt que dinsrer ce nombre en
dur dans le code. De cette faon, nous aurons modier uniquement ce nombre dans
le chier de conguration pour faire varier le nombre de news sur la page daccueil.
Un format pour le chier
Le format du chier sera le mme que le chier contenant les routes, savoir le format
XML. La base du chier sera celle-ci :
1 <?xml version="1.0" encoding="iso -8859 -1" ?>
2 <definitions >
3 </definitions >
Chaque paramtre se dclarera avec une balise define comme ceci :
1 <define var="nombre_news" value="5" />
Emplacement du chier
Le chier de conguration est propre chaque application. Par consquent, il devra
tre plac aux cts du chier routes.xml sous le doux nom de app.xml. Son chemin
complet sera donc /Applications/Nomdelapplication/Cong/app.xml.
Fonctionnement de la classe
Nous aurons donc une classe soccupant de grer la conguration. Pour faire simple,
nous nallons lui implmenter quune seule fonctionnalit : celle de rcuprer un para-
355
CHAPITRE 19. DVELOPPEMENT DE LA BIBLIOTHQUE
mtre. Il faut galement garder lesprit quil sagit dun composant de lapplica-
tion, donc il faut un lien de parent avec. . . Je suis sr que vous savez !
La mthode get($var) (qui sera charge de rcuprer la valeur dun paramtre) ne
devra pas parcourir chaque fois le chier de conguration, cela serait bien trop lourd.
Sil sagit du premier appel de la mthode, il faudra ouvrir le chier XML en instanciant
la classe DOMDocument et stocker tous les paramtres dans un attribut (admettons
$vars). Ainsi, chaque fois que la mthode get() sera invoque, nous naurons qu
retourner le paramtre prcdemment enregistr.
Notre classe, plutt simple, ressemble donc ceci (voir la gure 19.11).
Figure 19.11 Modlisation de la classe Cong
Codons
Voici le rsultat qui vous deviez obtenir :
1 <?php
2 namespace Library;
3
4 class Config extends ApplicationComponent
5 {
6 protected $vars = array();
7
8 public function get($var)
9 {
10 if (!$this ->vars)
11 {
12 $xml = new \DOMDocument;
13 $xml ->load(__DIR__.'/../ Applications/'.$this ->app ->name()
.'/Config/app.xml');
14
15 $elements = $xml ->getElementsByTagName('define ');
16
17 foreach ($elements as $element)
18 {
19 $this ->vars[$element ->getAttribute('var')] = $element ->
getAttribute('value');
356
BONUS 2 : LA CONFIGURATION
20 }
21 }
22
23 if (isset($this ->vars[$var]))
24 {
25 return $this ->vars[$var];
26 }
27
28 return null;
29 }
30 }
Il faut, comme nous lavons fait en crant la classe User, ajouter un nou-
vel attribut notre classe Application qui stockera linstance de Config.
Appelez-le par exemple $config (pour tre original :- ).
Notre bibliothque est crite, voil une bonne chose de faite ! Il ne reste plus qu crire
les applications et dvelopper les modules.
Cependant, avant de continuer, je vais massurer que vous me suiviez toujours. Voici
larborescence de larchitecture, avec des explications sur chaque dossier (voir la gure
19.12).
Figure 19.12 Arborescence
Il est possible que des dossiers vous paraissent sortir de nulle part. Cependant, je
vous assure que jen ai parl au moins une fois lors de la cration de certaines classes.
Nhsitez surtout pas relire le chapitre en vous appuyant sur cette arborescence, vous
comprendrez sans doute mieux.
357
CHAPITRE 19. DVELOPPEMENT DE LA BIBLIOTHQUE
358
Chapitre 20
Le frontend
Dicult :
N
ous allons enn aborder quelque chose de concret en construisant notre premire
application : le frontend. Cette application est la partie visible par tout le monde.
Nous allons construire un module de news avec commentaires, il y a de quoi faire,
donc ne perdons pas une seconde de plus !
Il est indispensable de connatre la classe DateTime. Cependant, ne vous inquitez pas,
cette classe est trs simple dutilisation, surtout que nous nutiliserons que deux mthodes
(le constructeur et la mthode format()).
359
CHAPITRE 20. LE FRONTEND
Lapplication
Nous allons commencer par crer les chiers de base dont nous aurons besoin. Vous
en connaissez quelques uns dj : la classe reprsentant lapplication, le layout, et les
deux chiers de conguration. Cependant, il nous en faut deux autres : un chier
qui instanciera notre classe et qui invoquera la mthode run(), et un .htaccess qui
redirigera toutes les pages sur ce chier. Nous verrons cela aprs avoir cr les quatre
chiers prcdemment cits.
La classe FrontendApplication
Commenons par crer notre classe FrontendApplication. Avant de commencer, assurez-
vous que vous avez bien cr le dossier /Applications/Frontend qui contiendra notre
application. Crez lintrieur le chier contenant notre classe, savoir FrontendAp-
plication.class.php.
Bien. Commencez par crire le minimum de la classe avec le namespace correspondant
(je le rappelle : le namespace est identique au chemin venant vers le chier contenant
la classe) en implmentant les deux mthodes crire, savoir __construct() (qui
aura pour simple contenu dappeler le constructeur parent puis de spcier le nom de
lapplication), et run(). Cette dernire mthode crira cette suite dinstruction :
Obtention du contrleur grce la mthode parente getController().
Excution du contrleur.
Assignation de la page cre par le contrleur la rponse.
Envoi de la rponse.
Votre classe devrait ressembler ceci :
1 <?php
2 namespace Applications\Frontend;
3
4 class FrontendApplication extends \Library\Application
5 {
6 public function __construct ()
7 {
8 parent :: __construct ();
9
10 $this ->name = 'Frontend ';
11 }
12
13 public function run()
14 {
15 $controller = $this ->getController ();
16 $controller ->execute ();
17
18 $this ->httpResponse ->setPage($controller ->page());
19 $this ->httpResponse ->send();
20 }
21 }
360
LAPPLICATION
Finalement, le droulement est assez simple quand on y regarde de plus prs.
Le layout
Tout site web qui se respecte se doit davoir un design. Nous nallons pas nous taler sur
ce type de cration, ce nest pas le sujet qui nous occupe. Nous allons donc nous servir
dun pack libre anciennement disponible sur un site de designs : jai nomm Envision.
Cest un design trs simple et facilement intgrable, idal pour ce que nous avons
faire. Vous pouvez le tlcharger grce au code web suivant :
Tlcharger Envision
Code web : 534847
Je vous laisse tlcharger le pack et dcouvrir les chiers quil contient. Pour rappel, les
chiers sont placer dans /Web. Vous devriez ainsi vous retrouver avec deux dossiers
dans /Web : /Web/css et /Web/images.
Revenons-en au layout. Celui-ci est assez simple et respecte les contraintes imposes
par le design.
1 <!DOCTYPE html PUBLIC " -//W3C//DTD XHTML 1.0 Strict //EN" "http
:// www.w3.org/TR/xhtml1/DTD/xhtml1 -strict.dtd">
2 <html xmlns="http :// www.w3.org/1999/xhtml" xml:lang="fr">
3 <head >
4 <title >
5 <?php if (! isset($title)) { ?>
6 Mon super site
7 <?php } else { echo $title; } ?>
8 </title >
9
10 <meta http -equiv="Content -type" content="text/html; charset
=iso -8859 -1" />
11
12 <link rel="stylesheet" href="/css/Envision.css" type="text/
css" />
13 </head >
14
15 <body >
16 <div id="wrap">
17 <div id="header">
18 <h1 id="logo -text"><a href="/">Mon super site </a></h1>
19 <p id="slogan">Comment a il ny a presque rien ? </
p>
20 </div >
21
22 <div id="menu">
23 <ul>
24 <li><a href="/">Accueil </a></li >
25 <?php if ($user ->isAuthenticated ()) { ?>
26 <li><a href="/admin/">Admin </a></li >
361
CHAPITRE 20. LE FRONTEND
27 <li><a href="/admin/news -insert.html">Ajouter une
news </a></li >
28 <?php } ?>
29 </ul>
30 </div >
31
32 <div id="content -wrap">
33 <div id="main">
34 <?php if ($user ->hasFlash ()) echo '<p style ="text -
align: center;">', $user ->getFlash (), '</p>'; ?>
35
36 <?php echo $content; ?>
37 </div >
38 </div >
39
40 <div id="footer"></div >
41 </div >
42 </body >
43 </html >
Quest-ce que cest que ces variables $user ?
La variable $user fait rfrence linstance de User. Elle doit tre initialise dans la
mthode getGeneratedPage() de la classe Page :
1 <?php
2 namespace Library;
3
4 class Page extends ApplicationComponent
5 {
6 // ...
7
8 public function getGeneratedPage ()
9 {
10 if (! file_exists($this ->contentFile))
11 {
12 throw new \InvalidArgumentException('La vue spcifie n\'
existe pas');
13 }
14
15 $user = $this ->app ->user();
16
17 extract($this ->vars);
18
19 ob_start ();
20 require $this ->contentFile;
21 $content = ob_get_clean ();
362
LAPPLICATION
22
23 ob_start ();
24 require dirname(__FILE__).'/../ Applications/'.$this ->app
->name().'/Templates/layout.php';
25 return ob_get_clean ();
26 }
27
28 // ...
29 }
Notez que si vous utilisez la variable $this, elle fera rfrence lobjet Page
car le layout est inclut dans la mthode Page::getGeneratedPage().
Les deux chiers de conguration
Nous allons prparer le terrain en crant les deux chiers de conguration dont nous
avons besoin : les chiers app.xml et routes.xml, pour linstant quasi-vierges :
1 <?xml version="1.0" encoding="iso -8859 -1" ?>
2 <definitions >
3 </definitions >
1 <?xml version="1.0" encoding="iso -8859 -1" ?>
2 <routes >
3 </routes >
Linstanciation de FrontendApplication
Pour instancier FrontendApplication, il va falloir crer un chier frontend.php. Ce
chier devra dabord inclure lautoload. Celui-ci aura donc pour simple contenu :
1 <?php
2 require '../ Library/autoload.php';
3
4 $app = new Applications\Frontend\FrontendApplication;
5 $app ->run();
Rcrire toutes les URL
Il faut que toutes les URL pointent vers ce chier. Pour cela, nous allons nous pencher
vers lURL rewriting. Voici le contenu du .htaccess :
1 RewriteEngine On
2
363
CHAPITRE 20. LE FRONTEND
3 # Si le fichier auquel on tente d'accder existe (si on veut
accder une image par exemple).
4 # Alors on ne rcrit pas l'URL.
5 RewriteCond %{ REQUEST_FILENAME} !-f
6 RewriteRule ^(.*)$ frontend.php [QSA ,L]
Le module de news
Nous allons commencer en douceur par un systme de news. Pourquoi en douceur ? Car
vous avez dj fait cet exercice lors du prcdent TP! Ainsi, nous allons voir comment
lintgrer au sein de lapplication, et vous verrez ainsi plus clair sur la manire dont
elle fonctionne. Pour ne perdre personne, nous allons refaire le TP petit petit pour
mieux lintgrer dans lapplication. Ainsi, je vais commencer par vous rappeler ce que
jattends du systme de news.
Fonctionnalits
Il doit tre possible dexcuter deux actions direntes sur le module de news :
Acher lindex du module. Cela aura pour eet de dvoiler les cinq dernires news
avec le titre et lextrait du contenu (seuls les 200 premiers caractres seront achs).
Acher une news spcique en cliquant sur son titre. Lauteur apparatra, ainsi que
la date de modication si la news a t modie.
Comme pour tout module, commencez par crer les dossiers et chiers de base, savoir :
Le dossier /Applications/Frontend/Modules/News qui contiendra notre mo-
dule.
Le chier /Applications/Frontend/Modules/News/NewsController.class.php
qui contiendra notre contrleur.
Le dossier /Applications/Frontend/Modules/News/Views qui contiendra les
vues.
Le chier /Library/Models/NewsManager.class.php qui contiendra notre ma-
nager de base ;
Le chier /Library/Models/NewsManager_PDO.class.php qui contiendra notre
manager utilisant PDO.
Le chier /Library/Entities/News.class.php qui contiendra la classe reprsen-
tant un enregistrement.
Structure de la table news
Une news est constitue dun titre, dun auteur et dun contenu. Aussi, il faut stocker
la date dajout de la news ainsi que sa date de modication. Cela nous donne une table
news ressemblant ceci :
1 CREATE TABLE IF NOT EXISTS news (
364
LE MODULE DE NEWS
2 id smallint(5) unsigned NOT NULL AUTO_INCREMENT ,
3 auteur varchar(30) COLLATE latin1_general_ci NOT NULL ,
4 titre varchar(100) COLLATE latin1_general_ci NOT NULL ,
5 contenu text COLLATE latin1_general_ci NOT NULL ,
6 dateAjout datetime NOT NULL ,
7 dateModif datetime NOT NULL ,
8 PRIMARY KEY (id )
9 ) DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci ;
Nous pouvons dsormais crire la classe reprsentant cette entit : News.
1 <?php
2 namespace Library\Entities;
3
4 class News extends \Library\Entity
5 {
6 protected $auteur ,
7 $titre ,
8 $contenu ,
9 $dateAjout ,
10 $dateModif;
11
12 const AUTEUR_INVALIDE = 1;
13 const TITRE_INVALIDE = 2;
14 const CONTENU_INVALIDE = 3;
15
16 public function isValid ()
17 {
18 return !(empty($this ->auteur) || empty($this ->titre) ||
empty($this ->contenu));
19 }
20
21
22 // SETTERS //
23
24 public function setAuteur($auteur)
25 {
26 if (! is_string($auteur) || empty($auteur))
27 {
28 $this ->erreurs [] = self:: AUTEUR_INVALIDE;
29 }
30 else
31 {
32 $this ->auteur = $auteur;
33 }
34 }
35
36 public function setTitre($titre)
37 {
38 if (! is_string($titre) || empty($titre))
39 {
365
CHAPITRE 20. LE FRONTEND
40 $this ->erreurs [] = self:: TITRE_INVALIDE;
41 }
42 else
43 {
44 $this ->titre = $titre;
45 }
46 }
47
48 public function setContenu($contenu)
49 {
50 if (! is_string($contenu) || empty($contenu))
51 {
52 $this ->erreurs [] = self:: CONTENU_INVALIDE;
53 }
54 else
55 {
56 $this ->contenu = $contenu;
57 }
58 }
59
60 public function setDateAjout (\ DateTime $dateAjout)
61 {
62 $this ->dateAjout = $dateAjout;
63 }
64
65 public function setDateModif (\ DateTime $dateModif)
66 {
67 $this ->dateModif = $dateModif;
68 }
69
70 // GETTERS //
71
72 public function auteur ()
73 {
74 return $this ->auteur;
75 }
76
77 public function titre ()
78 {
79 return $this ->titre;
80 }
81
82 public function contenu ()
83 {
84 return $this ->contenu;
85 }
86
87 public function dateAjout ()
88 {
89 return $this ->dateAjout;
366
LE MODULE DE NEWS
90 }
91
92 public function dateModif ()
93 {
94 return $this ->dateModif;
95 }
96 }
Laction index
La route
Commenons par implmenter cette action. La premire chose faire est de crer une
nouvelle route : quelle URL pointera vers cette action ? Je vous propose simplement
que ce soit la racine du site web, donc ce sera lURL /. Pour crer cette route, il va
falloir modier notre chier de conguration et y ajouter cette ligne :
1 <route url="/" module="News" action="index" />
Vient ensuite limplmentation de laction dans le contrleur.
Le contrleur
Qui dit nouvelle action dit nouvelle mthode, et cette mthode cest executeIndex().
Cette mthode devra rcuprer les cinq dernires news (le nombre cinq devra tre
stock dans le chier de conguration de lapplication, savoir /Applications/Fron-
tend/Cong/app.xml). Il faudra parcourir cette liste de news an de nassigner aux
news quun contenu de 200 caractres au maximum. Ensuite, il faut passer la liste des
news la vue :
1 <?php
2 namespace Applications\Frontend\Modules\News;
3
4 class NewsController extends \Library\BackController
5 {
6 public function executeIndex (\ Library\HTTPRequest $request)
7 {
8 $nombreNews = $this ->app ->config ()->get('nombre_news ');
9 $nombreCaracteres = $this ->app ->config ()->get('
nombre_caracteres ');
10
11 // On ajoute une dfinition pour le titre.
12 $this ->page ->addVar('title', 'Liste des '.$nombreNews.'
dernires news');
13
14 // On rcupre le manager des news.
15 $manager = $this ->managers ->getManagerOf('News');
16
367
CHAPITRE 20. LE FRONTEND
17 // Cette ligne , vous ne pouviez pas la deviner sachant qu'
on n'a pas encore touch au modle.
18 // Contentez -vous donc d'crire cette instruction , nous
implmenterons la mthode ensuite.
19 $listeNews = $manager ->getList(0, $nombreNews);
20
21 foreach ($listeNews as $news)
22 {
23 if (strlen($news ->contenu ()) > $nombreCaracteres)
24 {
25 $debut = substr($news ->contenu (), 0, $nombreCaracteres)
;
26 $debut = substr($debut , 0, strrpos($debut , ' ')) . '...
';
27
28 $news ->setContenu($debut);
29 }
30 }
31
32 // On ajoute la variable $listeNews la vue.
33 $this ->page ->addVar('listeNews ', $listeNews);
34 }
35 }
Comme vous le voyez, jutilise le chier de conguration pour rcuprer le nombre de
news acher et le nombre maximum de caractres. Voici le chier de conguration :
1 <?xml version="1.0" encoding="utf -8" ?>
2 <definitions >
3 <define var="nombre_news" value="5" />
4 <define var="nombre_caracteres" value="200" />
5 </definitions >
La vue
toute action correspond une vue du mme nom. Ici, la vue crer sera /Applica-
tions/Frontend/Modules/News/Views/index.php. Voici un exemple trs simple
pour cette vue :
1 <?php
2 foreach ($listeNews as $news)
3 {
4 ?>
5 <h2><a href="news -<?php echo $news['id ']; ?>.html"><?php echo
$news['titre']; ?></a></h2 >
6 <p><?php echo nl2br($news['contenu ']); ?></p>
7 <?php
8 }
368
LE MODULE DE NEWS
Notez lutilisation des news comme des tableaux : cela est possible du fait que lobjet
est une instance dune classe qui implmente ArrayAccess.
Le modle
Nous allons modier deux classes faisant partie du modle, savoir NewsManager et
NewsManager_PDO. Nous allons implmenter cette dernire classe une mthode :
getList(). Sa classe parente doit donc aussi tre modie pour dclarer cette m-
thode.
1 <?php
2 namespace Library\Models;
3
4 abstract class NewsManager extends \Library\Manager
5 {
6 /**
7 * Mthode retournant une liste de news demande
8 * @param $debut int La premire news slectionner
9 * @param $limite int Le nombre de news slectionner
10 * @return array La liste des news. Chaque entre est une
instance de News.
11 */
12 abstract public function getList($debut = -1, $limite = -1);
13 }
1 <?php
2 namespace Library\Models;
3
4 use \Library\Entities\News;
5
6 class NewsManager_PDO extends NewsManager
7 {
8 public function getList($debut = -1, $limite = -1)
9 {
10 $sql = 'SELECT id, auteur , titre , contenu , dateAjout ,
dateModif FROM news ORDER BY id DESC';
11
12 if ($debut != -1 || $limite != -1)
13 {
14 $sql .= ' LIMIT '.(int) $limite.' OFFSET '.(int) $debut;
15 }
16
17 $requete = $this ->dao ->query($sql);
18 $requete ->setFetchMode (\PDO:: FETCH_CLASS | \PDO::
FETCH_PROPS_LATE , '\Library\Entities\News');
19
20 $listeNews = $requete ->fetchAll ();
21
22 foreach ($listeNews as $news)
23 {
369
CHAPITRE 20. LE FRONTEND
24 $news ->setDateAjout(new \DateTime($news ->dateAjout ()));
25 $news ->setDateModif(new \DateTime($news ->dateModif ()));
26 }
27
28 $requete ->closeCursor ();
29
30 return $listeNews;
31 }
32 }
Vous pouvez faire le test : accdez la racine de votre site et vous dcouvrirez. . .
un gros blanc, car aucune news nest prsente en BDD :- . Vous pouvez en ajou-
ter manuellement via phpMyAdmin en attendant que nous ayons construit lespace
dadministration.
Si la constante PDO::FETCH_CLASS vous est inconnue, je vous invite relire
la premire partie du TP sur la ralisation dun systme de news.
Laction show
La route
Je vous propose que les URL du type news-id.html pointent vers cette action. Modiez
donc le chier de conguration des routes pour y ajouter celle-ci :
1 <?xml version="1.0" encoding="utf -8" ?>
2 <routes >
3 <route url="/" module="News" action="index" />
4 <route url="/news -([0-9]+)\.html" module="News" action="show"
vars="id"/>
5 </routes >
Le contrleur
Le contrleur implmentera la mthode executeShow(). Son contenu est simple : le
contrleur ira demander au manager la news correspondant lidentiant puis, il pas-
sera cette news la vue, en ayant pris soin de remplacer les sauts de lignes par des
balises <br> dans le contenu de la news.
Si la news nexiste pas, il faudra rediriger lutilisateur vers une erreur 404.
1 <?php
2 namespace Applications\Frontend\Modules\News;
370
LE MODULE DE NEWS
3
4 class NewsController extends \Library\BackController
5 {
6 public function executeIndex (\ Library\HTTPRequest $request)
7 {
8 $nombreNews = $this ->app ->config ()->get('nombre_news ');
9 $nombreCaracteres = $this ->app ->config ()->get('
nombre_caracteres ');
10
11 // On ajoute une dfinition pour le titre.
12 $this ->page ->addVar('title', 'Liste des '.$nombreNews.'
dernires news');
13
14 // On rcupre le manager des news.
15 $manager = $this ->managers ->getManagerOf('News');
16
17 $listeNews = $manager ->getList(0, $nombreNews);
18
19 foreach ($listeNews as $news)
20 {
21 if (strlen($news ->contenu ()) > $nombreCaracteres)
22 {
23 $debut = substr($news ->contenu (), 0, $nombreCaracteres)
;
24 $debut = substr($debut , 0, strrpos($debut , ' ')) . '...
';
25
26 $news ->setContenu($debut);
27 }
28 }
29
30 // On ajoute la variable $listeNews la vue.
31 $this ->page ->addVar('listeNews ', $listeNews);
32 }
33
34 public function executeShow (\ Library\HTTPRequest $request)
35 {
36 $news = $this ->managers ->getManagerOf('News')->getUnique(
$request ->getData('id'));
37
38 if (empty($news))
39 {
40 $this ->app ->httpResponse ()->redirect404 ();
41 }
42
43 $this ->page ->addVar('title', $news ->titre());
44 $this ->page ->addVar('news', $news);
45 }
46 }
371
CHAPITRE 20. LE FRONTEND
La vue
La vue se contente de grer lachage de la news. Faites comme bon vous semble, cela
na pas trop dimportance. Voici la version que je vous propose :
1 <p>Par <em ><?php echo $news['auteur ']; ?></em>, le <?php echo
$news['dateAjout ']->format('d/m/Y H\hi'); ?></p>
2 <h2 ><?php echo $news['titre']; ?></h2>
3 <p><?php echo nl2br($news['contenu ']); ?></p>
4
5 <?php if ($news['dateAjout '] != $news['dateModif ']) { ?>
6 <p style="text -align: right;"><small ><em>Modifie le <?php
echo $news['dateModif ']->format('d/m/Y H\hi'); ?></em ></
small ></p>
7 <?php } ?>
Le modle
Nous allons l aussi toucher nos classes NewsManager et NewsManager_PDO en ajoutant
la mthode getUnique().
1 <?php
2 namespace Library\Models;
3
4 use \Library\Entities\News;
5
6 abstract class NewsManager extends \Library\Manager
7 {
8 /**
9 * Mthode retournant une liste de news demande.
10 * @param $debut int La premire news slectionner
11 * @param $limite int Le nombre de news slectionner
12 * @return array La liste des news. Chaque entre est une
instance de News.
13 */
14 abstract public function getList($debut = -1, $limite = -1);
15
16 /**
17 * Mthode retournant une news prcise.
18 * @param $id int L'identifiant de la news rcuprer
19 * @return News La news demande
20 */
21 abstract public function getUnique($id);
22 }
1 <?php
2 namespace Library\Models;
3
4 use \Library\Entities\News;
5
372
LE MODULE DE NEWS
6 class NewsManager_PDO extends NewsManager
7 {
8 public function getList($debut = -1, $limite = -1)
9 {
10 $sql = 'SELECT id, auteur , titre , contenu , dateAjout ,
dateModif FROM news ORDER BY id DESC';
11
12 if ($debut != -1 || $limite != -1)
13 {
14 $sql .= ' LIMIT '.(int) $limite.' OFFSET '.(int) $debut;
15 }
16
17 $requete = $this ->dao ->query($sql);
18 $requete ->setFetchMode (\PDO:: FETCH_CLASS | \PDO::
FETCH_PROPS_LATE , '\Library\Entities\News');
19
20 $listeNews = $requete ->fetchAll ();
21
22 foreach ($listeNews as $news)
23 {
24 $news ->setDateAjout(new \DateTime($news ->dateAjout ()));
25 $news ->setDateModif(new \DateTime($news ->dateModif ()));
26 }
27
28 $requete ->closeCursor ();
29
30 return $listeNews;
31 }
32
33 public function getUnique($id)
34 {
35 $requete = $this ->dao ->prepare('SELECT id, auteur , titre ,
contenu , dateAjout , dateModif FROM news WHERE id = :id')
;
36 $requete ->bindValue(':id', (int) $id , \PDO:: PARAM_INT);
37 $requete ->execute ();
38
39 $requete ->setFetchMode (\PDO:: FETCH_CLASS | \PDO::
FETCH_PROPS_LATE , '\Library\Entities\News');
40
41 if ($news = $requete ->fetch())
42 {
43 $news ->setDateAjout(new \DateTime($news ->dateAjout ()));
44 $news ->setDateModif(new \DateTime($news ->dateModif ()));
45
46 return $news;
47 }
48
49 return null;
50 }
373
CHAPITRE 20. LE FRONTEND
51 }
Ajoutons des commentaires
Cahier des charges
Nous allons ajouter une action notre module de news : lajout dun commentaire. Il
ne faudra pas oublier de modier notre module de news, et plus spcialement laction
show pour laisser apparatre la liste des commentaires ainsi quun lien menant au
formulaire dajout de commentaire.
Avant toute chose, il va falloir crer les modles nous permettant dinteragir avec la
BDD pour accder aux commentaires :
Le chier /Library/Models/CommentsManager.class.php qui contiendra notre
manager de base.
Le chier /Library/Models/CommentsManager_PDO.class.php qui inclura
notre manager utilisant PDO.
Le chier /Library/Entities/Comment.class.php qui comportera la classe re-
prsentant un enregistrement.
Structure de la table comments
Un commentaire est assign une news. Il est constitu dun auteur et dun contenu,
ainsi que de sa date denregistrement. Notre table comments doit donc tre constitue
de la sorte :
1 CREATE TABLE IF NOT EXISTS comments (
2 id mediumint(9) NOT NULL AUTO_INCREMENT ,
3 news smallint(6) NOT NULL ,
4 auteur varchar(50) COLLATE latin1_general_ci NOT NULL ,
5 contenu text COLLATE latin1_general_ci NOT NULL ,
6 date datetime NOT NULL ,
7 PRIMARY KEY (id )
8 ) DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci ;
Puisque nous connaissons la structure dun commentaire, nous pouvons crire une
partie du modle, savoir la classe Comment :
1 <?php
2 namespace Library\Entities;
3
4 class Comment extends \Library\Entity
5 {
6 protected $news ,
7 $auteur ,
8 $contenu ,
9 $date;
374
AJOUTONS DES COMMENTAIRES
10
11 const AUTEUR_INVALIDE = 1;
12 const CONTENU_INVALIDE = 2;
13
14 public function isValid ()
15 {
16 return !(empty($this ->auteur) || empty($this ->contenu));
17 }
18
19 // SETTERS
20
21 public function setNews($news)
22 {
23 $this ->news = (int) $news;
24 }
25
26 public function setAuteur($auteur)
27 {
28 if (! is_string($auteur) || empty($auteur))
29 {
30 $this ->erreurs [] = self:: AUTEUR_INVALIDE;
31 }
32 else
33 {
34 $this ->auteur = $auteur;
35 }
36 }
37
38 public function setContenu($contenu)
39 {
40 if (! is_string($contenu) || empty($contenu))
41 {
42 $this ->erreurs [] = self:: CONTENU_INVALIDE;
43 }
44 else
45 {
46 $this ->contenu = $contenu;
47 }
48 }
49
50 public function setDate (\ DateTime $date)
51 {
52 $this ->date = $date;
53 }
54
55 // GETTERS
56
57 public function news()
58 {
59 return $this ->news;
375
CHAPITRE 20. LE FRONTEND
60 }
61
62 public function auteur ()
63 {
64 return $this ->auteur;
65 }
66
67 public function contenu ()
68 {
69 return $this ->contenu;
70 }
71
72 public function date()
73 {
74 return $this ->date;
75 }
76 }
Laction insertComment
La route
Nous nallons pas faire dans la fantaisie pour cette action, nous allons prendre une
URL basique : commenter-idnews.html. Rajoutez donc cette nouvelle route dans le
chier de conguration des routes :
1 <?xml version="1.0" encoding="utf -8" ?>
2 <routes >
3 <route url="/" module="News" action="index" />
4 <route url="/news -([0-9]+)\.html" module="News" action="show"
vars="id"/>
5 <route url="/commenter -([0-9]+)\.html" module="News" action="
insertComment" vars="news" />
6 </routes >
La vue
Dans un premier temps, nous allons nous attarder sur la vue car cest lintrieur de
celle-ci que nous allons construire le formulaire. Cela nous permettra donc de savoir
quels champs seront traiter par le contrleur. Je vous propose un formulaire trs
simple demandant le pseudo et le contenu lutilisateur :
1 <h2>Ajouter un commentaire </h2 >
2 <form action="" method="post">
3 <p>
4 <?php if (isset($erreurs) && in_array (\ Library\Entities\
Comment :: AUTEUR_INVALIDE , $erreurs)) echo 'L\'auteur est
invalide.<br />'; ?>
376
AJOUTONS DES COMMENTAIRES
5 <label >Pseudo </label >
6 <input type="text" name="pseudo" value=" <?php if (isset(
$comment)) echo htmlspecialchars($comment['auteur ']); ?>
" /><br />
7
8 <?php if (isset($erreurs) && in_array (\ Library\Entities\
Comment :: CONTENU_INVALIDE , $erreurs)) echo 'Le contenu
est invalide.<br />'; ?>
9 <label >Contenu </label >
10 <textarea name="contenu" rows="7" cols="50" ><?php if (isset
($comment)) echo htmlspecialchars($comment['contenu ']);
?></textarea ><br />
11
12 <input type="submit" value="Commenter" />
13 </p>
14 </form >
Lidentiant de la news est stock dans lURL. Puisque nous allons envoyer le formulaire
sur cette mme page, lidentiant de la news sera toujours prsent dans lURL et donc
accessible via le contrleur.
Le contrleur
Notre mthode executeInsertComment() se chargera dans un premier temps de vri-
er si le formulaire a t envoy en vriant si la variable POST pseudo existe. Ensuite,
elle procdera la vrication des donnes et insrera le commentaire en BDD si toutes
les donnes sont valides.
1 <?php
2 namespace Applications\Frontend\Modules\News;
3
4 class NewsController extends \Library\BackController
5 {
6 // ...
7
8 public function executeInsertComment (\ Library\HTTPRequest
$request)
9 {
10 $this ->page ->addVar('title', 'Ajout d\'un commentaire ');
11
12 if ($request ->postExists('pseudo '))
13 {
14 $comment = new \Library\Entities\Comment(array(
15 'news' => $request ->getData('news'),
16 'auteur ' => $request ->postData('pseudo '),
17 'contenu ' => $request ->postData('contenu ')
18 ));
19
20 if ($comment ->isValid ())
21 {
377
CHAPITRE 20. LE FRONTEND
22 $this ->managers ->getManagerOf('Comments ')->save(
$comment);
23
24 $this ->app ->user()->setFlash('Le commentaire a bien t
ajout, merci !');
25
26 $this ->app ->httpResponse ()->redirect('news -'.$request ->
getData('news').'.html');
27 }
28 else
29 {
30 $this ->page ->addVar('erreurs ', $comment ->erreurs ());
31 }
32
33 $this ->page ->addVar('comment ', $comment);
34 }
35 }
36
37 // ...
38 }
Le modle
Nous aurons besoin dimplmenter une mthode dans notre classe CommentsManager :
save(). En fait, il sagit dun raccourci , cette mthode appelle elle-mme une autre
mthode : add() ou modify() selon si le commentaire est dj prsent en BDD. Notre
manager peut savoir si lenregistrement est dj enregistr ou pas grce la mthode
isNew().
1 <?php
2 namespace Library\Models;
3
4 use \Library\Entities\Comment;
5
6 abstract class CommentsManager extends \Library\Manager
7 {
8 /**
9 * Mthode permettant d'ajouter un commentaire
10 * @param $comment Le commentaire ajouter
11 * @return void
12 */
13 abstract protected function add(Comment $comment);
14
15 /**
16 * Mthode permettant d'enregistrer un commentaire.
17 * @param $comment Le commentaire enregistrer
18 * @return void
19 */
20 public function save(Comment $comment)
378
AJOUTONS DES COMMENTAIRES
21 {
22 if ($comment ->isValid ())
23 {
24 $comment ->isNew() ? $this ->add($comment) : $this ->modify(
$comment);
25 }
26 else
27 {
28 throw new \RuntimeException('Le commentaire doit tre
valid pour tre enregistr');
29 }
30 }
31 }
1 <?php
2 namespace Library\Models;
3
4 use \Library\Entities\Comment;
5
6 class CommentsManager_PDO extends CommentsManager
7 {
8 protected function add(Comment $comment)
9 {
10 $q = $this ->dao ->prepare('INSERT INTO comments SET news = :
news , auteur = :auteur , contenu = :contenu , date = NOW()
');
11
12 $q ->bindValue(':news', $comment ->news(), \PDO:: PARAM_INT);
13 $q ->bindValue(':auteur ', $comment ->auteur ());
14 $q ->bindValue(':contenu ', $comment ->contenu ());
15
16 $q ->execute ();
17
18 $comment ->setId($this ->dao ->lastInsertId ());
19 }
20 }
Limplmentation de la mthode modify() se fera lors de la construction de lespace
dadministration.
Achage des commentaires
Modication du contrleur
Il sut simplement de passer la liste des commentaires la vue. Une seule instruction
sut donc :
1 <?php
2 namespace Applications\Frontend\Modules\News;
3
379
CHAPITRE 20. LE FRONTEND
4 class NewsController extends \Library\BackController
5 {
6 // ...
7
8 public function executeShow (\ Library\HTTPRequest $request)
9 {
10 $news = $this ->managers ->getManagerOf('News')->getUnique(
$request ->getData('id'));
11
12 if (empty($news))
13 {
14 $this ->app ->httpResponse ()->redirect404 ();
15 }
16
17 $this ->page ->addVar('title', $news ->titre());
18 $this ->page ->addVar('news', $news);
19 $this ->page ->addVar('comments ', $this ->managers ->
getManagerOf('Comments ')->getListOf($news ->id()));
20 }
21 }
Modication de la vue achant une news
La vue devra parcourir la liste des commentaires passs pour les acher. Les liens
pointant vers lajout dun commentaire devront aussi gurer sur la page.
1 <p>Par <em ><?php echo $news['auteur ']; ?></em>, le <?php echo
$news['dateAjout ']->format('d/m/Y H\hi'); ?></p>
2 <h2 ><?php echo $news['titre']; ?></h2>
3 <p><?php echo nl2br($news['contenu ']); ?></p>
4
5 <?php if ($news['dateAjout '] != $news['dateModif ']) { ?>
6 <p style="text -align: right;"><small ><em>Modifie le <?php
echo $news['dateModif ']->format('d/m/Y H\hi'); ?></em ></
small ></p>
7 <?php } ?>
8
9 <p><a href="commenter -<?php echo $news['id ']; ?>.html">Ajouter
un commentaire </a></p>
10
11 <?php
12 if (empty($comments))
13 {
14 ?>
15 <p>Aucun commentaire na encore t post. Soyez le premier
en laisser un !</p>
16 <?php
17 }
18
380
AJOUTONS DES COMMENTAIRES
19 foreach ($comments as $comment)
20 {
21 ?>
22 <fieldset >
23 <legend >
24 Post par <strong ><?php echo htmlspecialchars($comment['
auteur ']); ?></strong > le <?php echo $comment['date'
]->format('d/m/Y H\hi'); ?>
25 </legend >
26 <p><?php echo nl2br(htmlspecialchars($comment['contenu ']));
?></p>
27 </fieldset >
28 <?php
29 }
30 ?>
31
32 <p><a href="commenter -<?php echo $news['id ']; ?>.html">Ajouter
un commentaire </a></p>
Modication du manager des commentaires
Le manager des commentaires devra implmenter la mthode getListOf() dont a
besoin notre contrleur pour bien fonctionner. Voici la version que je vous propose :
1 <?php
2 namespace Library\Models;
3
4 use \Library\Entities\Comment;
5
6 abstract class CommentsManager extends \Library\Manager
7 {
8 /**
9 * Mthode permettant d'ajouter un commentaire.
10 * @param $comment Le commentaire ajouter
11 * @return void
12 */
13 abstract protected function add(Comment $comment);
14
15 /**
16 * Mthode permettant d'enregistrer un commentaire.
17 * @param $comment Le commentaire enregistrer
18 * @return void
19 */
20 public function save(Comment $comment)
21 {
22 if ($comment ->isValid ())
23 {
24 $comment ->isNew() ? $this ->add($comment) : $this ->modify(
$comment);
381
CHAPITRE 20. LE FRONTEND
25 }
26 else
27 {
28 throw new \RuntimeException('Le commentaire doit tre
valid pour tre enregistr');
29 }
30 }
31
32 /**
33 * Mthode permettant de rcuprer une liste de commentaires.
34 * @param $news La news sur laquelle on veut rcuprer les
commentaires
35 * @return array
36 */
37 abstract public function getListOf($news);
38 }
1 <?php
2 namespace Library\Models;
3
4 use \Library\Entities\Comment;
5
6 class CommentsManager_PDO extends CommentsManager
7 {
8 protected function add(Comment $comment)
9 {
10 $q = $this ->dao ->prepare('INSERT INTO comments SET news = :
news , auteur = :auteur , contenu = :contenu , date = NOW()
');
11
12 $q->bindValue(':news', $comment ->news(), \PDO:: PARAM_INT);
13 $q->bindValue(':auteur ', $comment ->auteur ());
14 $q->bindValue(':contenu ', $comment ->contenu ());
15
16 $q->execute ();
17
18 $comment ->setId($this ->dao ->lastInsertId ());
19 }
20
21 public function getListOf($news)
22 {
23 if (! ctype_digit($news))
24 {
25 throw new \InvalidArgumentException('L\'identifiant de la
news pass doit tre un nombre entier valide ');
26 }
27
28 $q = $this ->dao ->prepare('SELECT id, news , auteur , contenu ,
date FROM comments WHERE news = :news');
29 $q->bindValue(':news', $news , \PDO:: PARAM_INT);
382
AJOUTONS DES COMMENTAIRES
30 $q ->execute ();
31
32 $q ->setFetchMode (\PDO:: FETCH_CLASS | \PDO:: FETCH_PROPS_LATE
, '\Library\Entities\Comment ');
33
34 $comments = $q->fetchAll ();
35
36 foreach ($comments as $comment)
37 {
38 $comment ->setDate(new \DateTime($comment ->date()));
39 }
40
41 return $comments;
42 }
43 }
383
CHAPITRE 20. LE FRONTEND
384
Chapitre 21
Le backend
Dicult :
N
otre application est compose dun systme de news avec commentaires. Or, nous
ne pouvons actuellement pas ajouter de news, ni modrer les commentaires. Pour
ce faire, nous allons crer un espace dadministration, qui nest autre ici que le ba-
ckend. Cet espace dadministration sera ainsi compos dun systme de gestion de news
(ajout, modication et suppression) ainsi que dun systme de gestion de commentaires
(modication et suppression).
Ayant dj cr le frontend, ce chapitre devrait tre plus facile pour vous. Nanmoins, une
nouveaut fait son apparition : celle dinterdire le contenu de lapplication aux visiteurs. Ne
tranons donc pas, nous avons pas mal de travail qui nous attend !
385
CHAPITRE 21. LE BACKEND
Lapplication
Comme pour lapplication Frontend, nous aurons besoin de crer les chiers de base : la
classe reprsentant lapplication, le layout, les deux chiers de conguration et le chier
qui instanciera notre classe. Assurez-vous galement davoir cr le dossier /Applica-
tions/Backend.
La classe BackendApplication
Cette classe ne sera pas strictement identique la classe FrontendApplication. En
eet, nous devons scuriser lapplication an que seuls les utilisateurs authentis y
aient accs.
Pour rappel, voici le fonctionnement de la mthode run() de la classe FrontendAp-
plication :
Obtention du contrleur grce la mthode parente getController().
Excution du contrleur.
Assignation de la page cre par le contrleur la rponse.
Envoi de la rponse.
La classe BackendApplication fonctionnera de la mme faon, la dirence prs
que la premire instruction ne sera excute que si lutilisateur est authenti. Si-
non, nous allons rcuprer le contrleur du module de connexion que nous allons
crer dans ce chapitre. Voici donc le fonctionnement de la mthode run() de la classe
BackendApplication :
Si lutilisateur est authenti :
obtention du contrleur grce la mthode parente getController().
Sinon :
instanciation du contrleur du module de connexion.
Excution du contrleur.
Assignation de la page cre par le contrleur la rponse.
Envoi de la rponse.
Aide : nous avons un attribut $user dans notre classe qui reprsente lutilisa-
teur. Regardez les mthodes que nous lui avons implmentes si vous ne savez
pas comment on peut savoir sil est authenti ou non. Rappel : noubliez
pas dinclure lautoload au dbut du chier !
Vous devriez avoir une classe de ce type :
1 <?php
2 namespace Applications\Backend;
3
4 class BackendApplication extends \Library\Application
5 {
6 public function __construct ()
7 {
386
LAPPLICATION
8 parent :: __construct ();
9
10 $this ->name = 'Backend ';
11 }
12
13 public function run()
14 {
15 if ($this ->user ->isAuthenticated ())
16 {
17 $controller = $this ->getController ();
18 }
19 else
20 {
21 $controller = new Modules\Connexion\ConnexionController(
$this , 'Connexion ', 'index');
22 }
23
24 $controller ->execute ();
25
26 $this ->httpResponse ->setPage($controller ->page());
27 $this ->httpResponse ->send();
28 }
29 }
Le layout
Le layout est le mme que celui du frontend. Sachez quen pratique, cela est rare et
vous aurez gnralement deux layouts dirents (chaque application a ses spcicits).
Cependant, ici il nest pas ncessaire de faire deux chiers dirents. Vous pouvez donc
soit copier/coller le layout du frontend dans le dossier /Applications/Backend/-
Templates, soit crer le layout et inclure celui du frontend :
1 <?php require dirname(__FILE__).'/../../ Frontend/Templates/
layout.php';
Les deux chiers de conguration
L aussi il faut crer les deux chiers de conguration. Mettez-y, comme nous lavons
fait au prcdent chapitre, les structures de base.
1 <?xml version="1.0" encoding="iso -8859 -1" ?>
2 <definitions >
3 </definitions >
1 <?xml version="1.0" encoding="iso -8859 -1" ?>
2 <routes >
3 </routes >
387
CHAPITRE 21. LE BACKEND
Linstanciation de BackendApplication
Linstanciation de BackendApplication se fera dans le chier /Web/backend.php,
comme nous avons procd pour linstanciation de FrontendApplication.
Rappel : pensez inclure lautoload avant dinstancier la classe
BackendApplication !
1 <?php
2 require '../ Library/autoload.php';
3
4 $app = new Applications\Backend\BackendApplication;
5 $app ->run();
Rcrire les URL
Nous allons maintenant modier le chier .htaccess. Actuellement, toutes les URL sont
rediriges vers frontend.php. Nous garderons toujours cette rgle, mais nous allons
dabord en ajouter une autre : nous allons rediriger toutes les URL commenant par
admin/ vers backend.php. Vous tes libres de choisir un autre prxe, mais il faut
bien choisir quelque chose pour slectionner lapplication concerne par lURL.
Le .htaccess ressemble donc ceci :
1 RewriteEngine On
2
3 RewriteRule ^admin/ backend.php [QSA ,L]
4
5 RewriteCond %{ REQUEST_FILENAME} !-f
6
7 RewriteRule ^(.*)$ frontend.php [QSA ,L]
Le module de connexion
Ce module est un peu particulier. En eet, aucune route ne sera dnie pour pointer
vers ce module. De plus, ce module ne ncessite aucun stockage de donnes, nous
naurons donc pas de modle. La seule fonctionnalit attendue du module est dacher
son index. Cet index aura pour rle dacher le formulaire de connexion et de traiter
les donnes de ce formulaire.
Avant de commencer construire le module, nous allons prparer le terrain. O stocker
lidentiant et le mot de passe permettant daccder lapplication? Je vous propose
tout simplement dajouter deux dnitions dans le chier de conguration de lappli-
cation :
388
LE MODULE DE CONNEXION
1 <?xml version="1.0" encoding="utf -8" ?>
2 <definitions >
3 <define var="login" value="admin" />
4 <define var="pass" value="mdp" />
5 </definitions >
Vous pouvez maintenant, si ce nest pas dj fait, crer le dossier /Applications/Ba-
ckend/Modules/Connexion.
La vue
Nous allons dbuter par crer la vue correspondant lindex du module. Ce sera un
simple formulaire demandant le nom dutilisateur et le mot de passe linternaute :
1 <h2>Connexion </h2 >
2
3 <form action="" method="post">
4 <label >Pseudo </label >
5 <input type="text" name="login" /><br />
6
7 <label >Mot de passe </label >
8 <input type="password" name="password" /><br /><br />
9
10 <input type="submit" value="Connexion" />
11 </form >
Le contrleur
Procdons maintenant llaboration du contrleur. Ce contrleur implmentera une
seule mthode : executeIndex(). Cette mthode devra, si le formulaire a t envoy,
vrier si le pseudo et le mot de passe entrs sont corrects. Si cest le cas, lutilisateur
est authenti, sinon un message derreur sache.
1 <?php
2 namespace Applications\Backend\Modules\Connexion;
3
4 class ConnexionController extends \Library\BackController
5 {
6 public function executeIndex (\ Library\HTTPRequest $request)
7 {
8 $this ->page ->addVar('title', 'Connexion ');
9
10 if ($request ->postExists('login'))
11 {
12 $login = $request ->postData('login');
13 $password = $request ->postData('password ');
14
15 if ($login == $this ->app ->config ()->get('login') &&
$password == $this ->app ->config ()->get('pass'))
389
CHAPITRE 21. LE BACKEND
16 {
17 $this ->app ->user()->setAuthenticated(true);
18 $this ->app ->httpResponse ()->redirect('.');
19 }
20 else
21 {
22 $this ->app ->user()->setFlash('Le pseudo ou le mot de
passe est incorrect.');
23 }
24 }
25 }
26 }
Ce fut court, mais essentiel. Nous venons de scuriser en un rien de temps lapplication
toute entire. De plus, ce module est rutilisable dans dautres projets ! En eet, rien
ne le lie cette application. partir du moment o lapplication aura un chier de
conguration adapt (cest--dire quil a dclar les variables login et pass), alors elle
pourra sen servir.
Le module de news
Nous allons maintenant attaquer le module de news sur notre application. Comme
dhabitude, nous commenons par la liste des fonctionnalits attendues.
Fonctionnalits
Ce module doit nous permettre de grer le contenu de la base de donnes. Par cons-
quent, nous devons avoir quatre actions :
Laction index qui nous ache la liste des news avec des liens pour les modier ou
supprimer.
Laction insert pour ajouter une news.
Laction update pour modier une news.
Laction delete pour supprimer une news.
Laction index
La route
Tout dabord, dnissons lURL qui pointera vers cette action. Je vous propose tout
simplement que ce soit laccueil de lespace dadministration :
1 <?xml version="1.0" encoding="utf -8" ?>
2 <routes >
3 <route url="/admin/" module="News" action="index" />
4 </routes >
390
LE MODULE DE NEWS
Le contrleur
Le contrleur se chargera uniquement de passer la liste des news la vue ainsi que le
nombre de news prsent. Le contenu de la mthode est donc assez simple :
1 <?php
2 namespace Applications\Backend\Modules\News;
3
4 class NewsController extends \Library\BackController
5 {
6 public function executeIndex (\ Library\HTTPRequest $request)
7 {
8 $this ->page ->addVar('title', 'Gestion des news');
9
10 $manager = $this ->managers ->getManagerOf('News');
11
12 $this ->page ->addVar('listeNews ', $manager ->getList ());
13 $this ->page ->addVar('nombreNews ', $manager ->count());
14 }
15 }
Comme vous le voyez, nous nous resservons de la mthode getList() que nous avions
implmente au cours du prcdent chapitre au cours de la construction du frontend.
Cependant, il nous reste implmenter une mthode dans notre manager : count().
Le modle
La mthode count() est trs simple : elle ne fait quexcuter une requte pour renvoyer
le rsultat.
1 <?php
2 namespace Library\Models;
3
4 abstract class NewsManager extends \Library\Manager
5 {
6 /**
7 * Mthode renvoyant le nombre de news total.
8 * @return int
9 */
10 abstract public function count ();
11
12 // ...
13 }
1 <?php
2 namespace Library\Models;
3
4 class NewsManager_PDO extends NewsManager
5 {
6 public function count ()
391
CHAPITRE 21. LE BACKEND
7 {
8 return $this ->dao ->query('SELECT COUNT (*) FROM news')->
fetchColumn ();
9 }
10
11 // ...
12 }
La vue
La vue se contente de parcourir le tableau de news pour en acher les donnes. Faites
dans la simplicit.
1 <p style="text -align: center">Il y a actuellement <?php echo
$nombreNews; ?> news. En voici la liste :</p>
2
3 <table >
4 <tr><th>Auteur </th ><th>Titre </th><th >Date dajout </th ><th >
Dernire modification </th ><th >Action </th ></tr >
5 <?php
6 foreach ($listeNews as $news)
7 {
8 echo '<tr><td>', $news['auteur '], '</td><td>', $news['titre'
], '</td><td>le ', $news['dateAjout ']->format('d/m/Y H\
hi'), '</td><td>', ($news['dateAjout '] == $news['dateModif
'] ? '-' : 'le '.$news['dateModif ']->format('d/m/Y H\hi'
)), '</td><td><a href="news -update -', $news['id'], '.html
"><img src ="/ images/update.png" alt=" Modifier" /></a> <a
href="news -delete -', $news['id'], '.html"><img src="/
images/delete.png" alt=" Supprimer" /></a></td ></tr >', "\n"
;
9 }
10 ?>
11 </table >
Laction insert
La route
Je vous propose que lURL qui pointera vers cette action soit /admin/news-insert.html.
Je pense que maintenant vous savez comment dnir une route, mais je remets le chier
pour que tout le monde suive bien :
1 <?xml version="1.0" encoding="utf -8" ?>
2 <routes >
3 <route url="/admin/" module="News" action="index" />
4 <route url="/admin/news -insert \.html" module="News" action="
insert" />
392
LE MODULE DE NEWS
5 </routes >
Le contrleur
Le contrleur vrie si le formulaire a t envoy. Si cest le cas, alors il procdera
la vrication des donnes et insrera la news en BDD si tout est valide. Cependant,
il y a un petit problme : lorsque nous implmenterons laction update, nous allons
devoir rcrire la partie traitement du formulaire car la validation des donnes suit
la mme logique. Nous allons donc crer une autre mthode au sein du contrleur,
nomme processForm(), qui se chargera de traiter le formulaire et denregistrer la
news en BDD.
Rappel : le manager contient une mthode save() qui se chargera soit
dajouter la news si elle est nouvelle, soit de la mettre jour si elle est dj
enregistre. Cest cette mthode que vous devez invoquer.
1 <?php
2 namespace Applications\Backend\Modules\News;
3
4 class NewsController extends \Library\BackController
5 {
6 // ...
7
8 public function executeInsert (\ Library\HTTPRequest $request)
9 {
10 if ($request ->postExists('auteur '))
11 {
12 $this ->processForm($request);
13 }
14
15 $this ->page ->addVar('title', 'Ajout d\'une news');
16 }
17
18 public function processForm (\ Library\HTTPRequest $request)
19 {
20 $news = new \Library\Entities\News(
21 array(
22 'auteur ' => $request ->postData('auteur '),
23 'titre' => $request ->postData('titre'),
24 'contenu ' => $request ->postData('contenu ')
25 )
26 );
27
28 // L'identifiant de la news est transmis si on veut la
modifier.
29 if ($request ->postExists('id'))
30 {
31 $news ->setId($request ->postData('id'));
393
CHAPITRE 21. LE BACKEND
32 }
33
34 if ($news ->isValid ())
35 {
36 $this ->managers ->getManagerOf('News')->save($news);
37
38 $this ->app ->user()->setFlash($news ->isNew() ? 'La news a
bien t ajoute !' : 'La news a bien t modifie !')
;
39 }
40 else
41 {
42 $this ->page ->addVar('erreurs ', $news ->erreurs ());
43 }
44
45 $this ->page ->addVar('news', $news);
46 }
47 }
Le modle
Nous allons implmenter les mthodes save() et add() dans notre manager an que
notre contrleur puisse tre fonctionnel.
Rappel : la mthode save() simplmente directement dans NewsManager
puisquelle ne dpend pas du DAO.
1 <?php
2 namespace Library\Models;
3
4 use \Library\Entities\News;
5
6 abstract class NewsManager extends \Library\Manager
7 {
8 /**
9 * Mthode permettant d'ajouter une news.
10 * @param $news News La news ajouter
11 * @return void
12 */
13 abstract protected function add(News $news);
14
15 /**
16 * Mthode permettant d'enregistrer une news.
17 * @param $news News la news enregistrer
18 * @see self::add()
19 * @see self:: modify ()
20 * @return void
394
LE MODULE DE NEWS
21 */
22 public function save(News $news)
23 {
24 if ($news ->isValid ())
25 {
26 $news ->isNew() ? $this ->add($news) : $this ->modify($news)
;
27 }
28 else
29 {
30 throw new \RuntimeException('La news doit tre valide
pour tre enregistre');
31 }
32 }
33
34 // ...
35 }
1 <?php
2 namespace Library\Models;
3
4 use \Library\Entities\News;
5
6 class NewsManager_PDO extends NewsManager
7 {
8 protected function add(News $news)
9 {
10 $requete = $this ->dao ->prepare('INSERT INTO news SET auteur
= :auteur , titre = :titre , contenu = :contenu ,
dateAjout = NOW(), dateModif = NOW()');
11
12 $requete ->bindValue(':titre', $news ->titre());
13 $requete ->bindValue(':auteur ', $news ->auteur ());
14 $requete ->bindValue(':contenu ', $news ->contenu ());
15
16 $requete ->execute ();
17 }
18
19 // ...
20 }
La vue
L aussi, nous utiliserons de la duplication de code pour acher le formulaire. En eet,
la vue correspondant laction update devra galement acher ce formulaire. Nous
allons donc crer un chier qui contiendra ce formulaire et qui sera inclus au sein des
vues. Je vous propose de lappeler _form.php (le _ est ici utilis pour bien indiquer
quil ne sagit pas dune vue mais dun lment inclure).
395
CHAPITRE 21. LE BACKEND
1 <h2>Ajouter une news </h2>
2 <?php require '_form.php';
1 <form action="" method="post">
2 <p>
3 <?php if (isset($erreurs) && in_array (\ Library\Entities\
News:: AUTEUR_INVALIDE , $erreurs)) echo 'L\'auteur est
invalide.<br />'; ?>
4 <label >Auteur </label >
5 <input type="text" name="auteur" value=" <?php if (isset(
$news)) echo $news['auteur ']; ?>" /><br />
6
7 <?php if (isset($erreurs) && in_array (\ Library\Entities\
News:: TITRE_INVALIDE , $erreurs)) echo 'Le titre est
invalide.<br />'; ?>
8 <label >Titre </label ><input type="text" name="titre" value="
<?php if (isset($news)) echo $news['titre ']; ?>" /><br
/>
9
10 <?php if (isset($erreurs) && in_array (\ Library\Entities\
News:: CONTENU_INVALIDE , $erreurs)) echo 'Le contenu est
invalide.<br />'; ?>
11 <label >Contenu </label ><textarea rows="8" cols="60" name="
contenu" ><?php if (isset($news)) echo $news['contenu '];
?></textarea ><br />
12 <?php
13 if(isset($news) && !$news ->isNew())
14 {
15 ?>
16 <input type="hidden" name="id" value=" <?php echo $news['id
']; ?>" />
17 <input type="submit" value="Modifier" name="modifier" />
18 <?php
19 }
20 else
21 {
22 ?>
23 <input type="submit" value="Ajouter" />
24 <?php
25 }
26 ?>
27 </p>
28 </form >
396
LE MODULE DE NEWS
Laction update
La route
Pas doriginalit ici, nous allons choisir une URL basique : /admin/news-update-
id.html.
1 <?xml version="1.0" encoding="utf -8" ?>
2 <routes >
3 <route url="/admin/" module="News" action="index" />
4 <route url="/admin/news -insert \.html" module="News" action="
insert" />
5 <route url="/admin/news -update -([0-9]+)\.html" module="News"
action="update" vars="id" />
6 </routes >
Le contrleur
La mthode executeUpdate() est quasiment identique executeInsert(). La seule
dirence est quil faut passer la news la vue si le formulaire na pas t envoy.
1 <?php
2 namespace Applications\Backend\Modules\News;
3
4 class NewsController extends \Library\BackController
5 {
6 // ...
7
8 public function executeUpdate (\ Library\HTTPRequest $request)
9 {
10 if ($request ->postExists('auteur '))
11 {
12 $this ->processForm($request);
13 }
14 else
15 {
16 $this ->page ->addVar('news', $this ->managers ->getManagerOf
('News')->getUnique($request ->getData('id')));
17 }
18
19 $this ->page ->addVar('title', 'Modification d\'une news');
20 }
21
22 // ...
23 }
397
CHAPITRE 21. LE BACKEND
Le modle
Ce code fait appel deux mthodes : getUnique() et modify(). La premire a dj
t implmente au cours du prcdent chapitre et la seconde avait volontairement t
laisse de ct. Il est maintenant temps de limplmenter.
1 <?php
2 namespace Library\Models;
3
4 use \Library\Entities\News;
5
6 abstract class NewsManager extends \Library\Manager
7 {
8 // ...
9
10 /**
11 * Mthode permettant de modifier une news.
12 * @param $news news la news modifier
13 * @return void
14 */
15 abstract protected function modify(News $news);
16
17 // ...
18 }
1 <?php
2 namespace Library\Models;
3
4 use \Library\Entities\News;
5
6 class NewsManager_PDO extends NewsManager
7 {
8 // ...
9
10 protected function modify(News $news)
11 {
12 $requete = $this ->dao ->prepare('UPDATE news SET auteur = :
auteur , titre = :titre , contenu = :contenu , dateModif =
NOW() WHERE id = :id');
13
14 $requete ->bindValue(':titre', $news ->titre());
15 $requete ->bindValue(':auteur ', $news ->auteur ());
16 $requete ->bindValue(':contenu ', $news ->contenu ());
17 $requete ->bindValue(':id', $news ->id(), \PDO:: PARAM_INT);
18
19 $requete ->execute ();
20 }
21
22 // ...
23 }
398
LE MODULE DE NEWS
La vue
De la mme faon que pour laction insert, ce procd tient en deux lignes : il sagit
seulement dinclure le formulaire, cest tout !
1 <h2>Modifier une news </h2>
2 <?php require '_form.php';
Laction delete
La route
Pour continuer dans loriginalit, lURL qui pointera vers cette action sera du type
/admin/news-delete-id.html.
1 <?xml version="1.0" encoding="utf -8" ?>
2 <routes >
3 <route url="/admin/" module="News" action="index" />
4 <route url="/admin/news -insert \.html" module="News" action="
insert" />
5 <route url="/admin/news -update -([0-9]+)\.html" module="News"
action="update" vars="id" />
6 <route url="/admin/news -delete -([0-9]+)\.html" module="News"
action="delete" vars="id" />
7 </routes >
Le contrleur
Le contrleur se chargera dinvoquer la mthode du manager qui supprimera la news.
Ensuite, il redirigera lutilisateur laccueil de lespace dadministration en ayant pris
soin de spcier un message qui sachera au prochain chargement de page. Ainsi,
cette action ne possdera aucune vue.
1 <?php
2 namespace Applications\Backend\Modules\News;
3
4 class NewsController extends \Library\BackController
5 {
6 // ...
7
8 public function executeDelete (\ Library\HTTPRequest $request)
9 {
10 $this ->managers ->getManagerOf('News')->delete($request ->
getData('id'));
11
12 $this ->app ->user()->setFlash('La news a bien t supprime
!');
13
399
CHAPITRE 21. LE BACKEND
14 $this ->app ->httpResponse ()->redirect('.');
15 }
16
17 // ...
18 }
Le modle
Ici, une simple requte de type DELETE sut.
1 <?php
2 namespace Library\Models;
3
4 use \Library\Entities\News;
5
6 abstract class NewsManager extends \Library\Manager
7 {
8 // ...
9
10 /**
11 * Mthode permettant de supprimer une news.
12 * @param $id int L'identifiant de la news supprimer
13 * @return void
14 */
15 abstract public function delete($id);
16
17 // ...
18 }
1 <?php
2 namespace Library\Models;
3
4 use \Library\Entities\News;
5
6 class NewsManager_PDO extends NewsManager
7 {
8 // ...
9
10 public function delete($id)
11 {
12 $this ->dao ->exec('DELETE FROM news WHERE id = '.(int) $id);
13 }
14
15 // ...
16 }
400
NOUBLIONS PAS LES COMMENTAIRES!
Noublions pas les commentaires !
Finissons de construire notre application en implmentant les dernires fonctionnalits
permettant de grer les commentaires.
Fonctionnalits
Nous allons faire simple et implmenter deux fonctionnalits :
La modication de commentaires.
La suppression de commentaires.
Laction updateComment
La route
Commenons, comme dhabitude, par dnir lURL qui pointera vers ce module. Je
vous propose quelque chose de simple comme /admin/comments-update-id.html.
1 <?xml version="1.0" encoding="utf -8" ?>
2 <routes >
3 <route url="/admin/" module="News" action="index" />
4 <route url="/admin/news -insert \.html" module="News" action="
insert" />
5 <route url="/admin/news -update -([0-9]+)\.html" module="News"
action="update" vars="id" />
6 <route url="/admin/news -delete -([0-9]+)\.html" module="News"
action="delete" vars="id" />
7 <route url="/admin/comment -update -([0-9]+)\.html" module="
News" action="updateComment" vars="id" />
8 </routes >
Le contrleur
La mthode que lon implmentera aura pour rle de contrler les valeurs du formulaire
et de modier le commentaire en BDD si tout est valide. Vous devriez avoir quelque
chose de semblable ce que nous avons dans lapplication Frontend. Il faudra ensuite
rediriger lutilisateur sur la news quil lisait.
Aide : pour rediriger lutilisateur sur la news, il va falloir obtenir lidentiant
de cette dernire. Il faudra donc ajouter un champ cach dans le formulaire
pour transmettre ce paramtre.
1 <?php
2 namespace Applications\Backend\Modules\News;
3
401
CHAPITRE 21. LE BACKEND
4 class NewsController extends \Library\BackController
5 {
6 // ...
7
8 public function executeUpdateComment (\ Library\HTTPRequest
$request)
9 {
10 $this ->page ->addVar('title', 'Modification d\'un
commentaire ');
11
12 if ($request ->postExists('pseudo '))
13 {
14 $comment = new \Library\Entities\Comment(array(
15 'id' => $request ->getData('id'),
16 'auteur ' => $request ->postData('pseudo '),
17 'contenu ' => $request ->postData('contenu ')
18 ));
19
20 if ($comment ->isValid ())
21 {
22 $this ->managers ->getManagerOf('Comments ')->save(
$comment);
23
24 $this ->app ->user()->setFlash('Le commentaire a bien t
modifi !');
25
26 $this ->app ->httpResponse ()->redirect('/news -'.$request
->postData('news').'.html');
27 }
28 else
29 {
30 $this ->page ->addVar('erreurs ', $comment ->erreurs ());
31 }
32
33 $this ->page ->addVar('comment ', $comment);
34 }
35 else
36 {
37 $this ->page ->addVar('comment ', $this ->managers ->
getManagerOf('Comments ')->get($request ->getData('id'))
);
38 }
39 }
40
41 // ...
42 }
402
NOUBLIONS PAS LES COMMENTAIRES!
Le modle
Nous avons ici besoin dimplmenter deux mthodes : modify() et get(). La premire
se contente dexcuter une requte de type UPDATE et la seconde une requte de type
SELECT.
1 <?php
2 namespace Library\Models;
3
4 use \Library\Entities\Comment;
5
6 abstract class CommentsManager extends \Library\Manager
7 {
8 // ...
9
10 /**
11 * Mthode permettant de modifier un commentaire.
12 * @param $comment Le commentaire modifier
13 * @return void
14 */
15 abstract protected function modify(Comment $comment);
16
17 /**
18 * Mthode permettant d'obtenir un commentaire spcifique.
19 * @param $id L'identifiant du commentaire
20 * @return Comment
21 */
22 abstract public function get($id);
23 }
1 <?php
2 namespace Library\Models;
3
4 use \Library\Entities\Comment;
5
6 class CommentsManager_PDO extends CommentsManager
7 {
8 // ...
9
10 protected function modify(Comment $comment)
11 {
12 $q = $this ->dao ->prepare('UPDATE comments SET auteur = :
auteur , contenu = :contenu WHERE id = :id');
13
14 $q ->bindValue(':auteur ', $comment ->auteur ());
15 $q ->bindValue(':contenu ', $comment ->contenu ());
16 $q ->bindValue(':id', $comment ->id(), \PDO:: PARAM_INT);
17
18 $q ->execute ();
19 }
20
403
CHAPITRE 21. LE BACKEND
21 public function get($id)
22 {
23 $q = $this ->dao ->prepare('SELECT id, news , auteur , contenu
FROM comments WHERE id = :id');
24 $q->bindValue(':id', (int) $id , \PDO:: PARAM_INT);
25 $q->execute ();
26
27 $q->setFetchMode (\PDO:: FETCH_CLASS | \PDO:: FETCH_PROPS_LATE
, '\Library\Entities\Comment ');
28
29 return $q ->fetch();
30 }
31 }
La vue
La vue ne fera que contenir le formulaire et acher les erreurs sil y en a.
1 <form action="" method="post">
2 <p>
3 <?php if (isset($erreurs) && in_array (\ Library\Entities\
Comment :: AUTEUR_INVALIDE , $erreurs)) echo 'L\'auteur est
invalide.<br />'; ?>
4 <label >Pseudo </label ><input type="text" name="pseudo" value
="<?php echo htmlspecialchars($comment['auteur ']); ?>"
/><br />
5
6 <?php if (isset($erreurs) && in_array (\ Library\Entities\
Comment :: CONTENU_INVALIDE , $erreurs)) echo 'Le contenu
est invalide.<br />'; ?>
7 <label >Contenu </label ><textarea name="contenu" rows="7"
cols="50" ><?php echo htmlspecialchars($comment['contenu '
]); ?></textarea ><br />
8
9 <input type="hidden" name="news" value=" <?php echo $comment
['news ']; ?>" />
10 <input type="submit" value="Modifier" />
11 </p>
12 </form >
Modication de la vue de lachage des commentaires
Pour des raisons pratiques, il serait prfrable de modier lachage des commentaires
an dajouter un lien chacun menant vers la modication du commentaire. Pour
cela, il faudra modier la vue correspondant laction show du module news de
lapplication Frontend.
1 <p>Par <em ><?php echo $news['auteur ']; ?></em>, le <?php echo
$news['dateAjout ']->format('d/m/Y H\hi'); ?></p>
404
NOUBLIONS PAS LES COMMENTAIRES!
2 <h2 ><?php echo $news['titre']; ?></h2 >
3 <p><?php echo nl2br($news['contenu ']); ?></p>
4
5 <?php if ($news['dateAjout '] != $news['dateModif ']) { ?>
6 <p style="text -align: right;"><small ><em>Modifie le <?php
echo $news['dateModif ']->format('d/m/Y H\hi'); ?></em ></
small ></p>
7 <?php } ?>
8
9 <p><a href="commenter -<?php echo $news['id ']; ?>.html">Ajouter
un commentaire </a></p>
10
11 <?php
12 if (empty($comments))
13 {
14 ?>
15 <p>Aucun commentaire na encore t post. Soyez le premier
en laisser un !</p>
16 <?php
17 }
18
19 foreach ($comments as $comment)
20 {
21 ?>
22 <fieldset >
23 <legend >
24 Post par <strong ><?php echo htmlspecialchars($comment['
auteur ']); ?></strong > le <?php echo $comment['date'
]->format('d/m/Y H\hi'); ?>
25 <?php if ($user ->isAuthenticated ()) { ?> -
26 <a href="admin/comment -update -<?php echo $comment['id
']; ?>.html">Modifier </a>
27 <?php } ?>
28 </legend >
29 <p><?php echo nl2br(htmlspecialchars($comment['contenu ']));
?></p>
30 </fieldset >
31 <?php
32 }
33 ?>
34
35 <p><a href="commenter -<?php echo $news['id ']; ?>.html">Ajouter
un commentaire </a></p>
405
CHAPITRE 21. LE BACKEND
Laction deleteComment
La route
Faisons l aussi trs simple : nous navons qu prendre une URL de ce type :
/admin/comments-delete-id.html.
1 <?xml version="1.0" encoding="utf -8" ?>
2 <routes >
3 <route url="/admin/" module="News" action="index" />
4 <route url="/admin/news -insert \.html" module="News" action="
insert" />
5 <route url="/admin/news -update -([0-9]+)\.html" module="News"
action="update" vars="id" />
6 <route url="/admin/news -delete -([0-9]+)\.html" module="News"
action="delete" vars="id" />
7 <route url="/admin/comment -update -([0-9]+)\.html" module="
News" action="updateComment" vars="id" />
8 <route url="/admin/comment -delete -([0-9]+)\.html" module="
News" action="deleteComment" vars="id" />
9 </routes >
Le contrleur
Il faut dans un premier temps invoquer la mthode du manager permettant de suppri-
mer un commentaire. Redirigez ensuite lutilisateur sur laccueil de lespace dadminis-
tration.
1 <?php
2 namespace Applications\Backend\Modules\News;
3
4 class NewsController extends \Library\BackController
5 {
6 // ...
7
8 public function executeDeleteComment (\ Library\HTTPRequest
$request)
9 {
10 $this ->managers ->getManagerOf('Comments ')->delete($request
->getData('id'));
11
12 $this ->app ->user()->setFlash('Le commentaire a bien t
supprim !');
13
14 $this ->app ->httpResponse ()->redirect('.');
15 }
16 }
Aucune vue nest donc ncessaire ici.
406
NOUBLIONS PAS LES COMMENTAIRES!
Le modle
Il sut ici dimplmenter la mthode delete() excutant une simple requte DELETE.
1 <?php
2 namespace Library\Models;
3
4 use \Library\Entities\Comment;
5
6 abstract class CommentsManager extends \Library\Manager
7 {
8 // ...
9
10 /**
11 * Mthode permettant de supprimer un commentaire.
12 * @param $id L'identifiant du commentaire supprimer
13 * @return void
14 */
15 abstract public function delete($id);
16
17 // ...
18 }
1 <?php
2 namespace Library\Models;
3
4 use \Library\Entities\Comment;
5
6 class CommentsManager_PDO extends CommentsManager
7 {
8 // ...
9
10 public function delete($id)
11 {
12 $this ->dao ->exec('DELETE FROM comments WHERE id = '.(int)
$id);
13 }
14
15 // ...
16 }
Modication de lachage des commentaires
Nous allons l aussi insrer le lien de suppression de chaque commentaire an de nous
faciliter la tche. Modiez donc la vue de laction show du module news de lapplica-
tion frontend :
1 <p>Par <em ><?php echo $news['auteur ']; ?></em>, le <?php echo
$news['dateAjout ']->format('d/m/Y H\hi'); ?></p>
2 <h2 ><?php echo $news['titre']; ?></h2 >
407
CHAPITRE 21. LE BACKEND
3 <p><?php echo nl2br($news['contenu ']); ?></p>
4
5 <?php if ($news['dateAjout '] != $news['dateModif ']) { ?>
6 <p style="text -align: right;"><small ><em>Modifie le <?php
echo $news['dateModif ']->format('d/m/Y H\hi'); ?></em ></
small ></p>
7 <?php } ?>
8
9 <p><a href="commenter -<?php echo $news['id ']; ?>.html">Ajouter
un commentaire </a></p>
10
11 <?php
12 if (empty($comments))
13 {
14 ?>
15 <p>Aucun commentaire na encore t post. Soyez le premier
en laisser un !</p>
16 <?php
17 }
18
19 foreach ($comments as $comment)
20 {
21 ?>
22 <fieldset >
23 <legend >
24 Post par <strong ><?php echo htmlspecialchars($comment['
auteur ']); ?></strong > le <?php echo $comment['date'
]->format('d/m/Y H\hi'); ?>
25 <?php if ($user ->isAuthenticated ()) { ?> -
26 <a href="admin/comment -update -<?php echo $comment['id
']; ?>.html">Modifier </a> |
27 <a href="admin/comment -delete -<?php echo $comment['id
']; ?>.html">Supprimer </a>
28 <?php } ?>
29 </legend >
30 <p><?php echo nl2br(htmlspecialchars($comment['contenu ']));
?></p>
31 </fieldset >
32 <?php
33 }
34 ?>
35
36 <p><a href="commenter -<?php echo $news['id ']; ?>.html">Ajouter
un commentaire </a></p>
408
Chapitre 22
Grer les formulaires
Dicult :
I
l est possible que quelque chose vous chionne un petit peu. En eet, dans le frontend,
nous avons cr un formulaire pour ajouter un commentaire. Dans le backend, nous
avons recr quasiment le mme : nous avons fait de la duplication de code. Or,
puisque vous tes un excellent programmeur, cela devrait vous piquer les yeux !
Pour pallier ce problme courant de duplication de formulaires, nous allons externaliser nos
formulaires laide dune API, cest--dire que le code crant le formulaire sera accessible,
un autre endroit, par nimporte quel module de nimporte quelle application. Cette technique
fera dune pierre deux coups : non seulement nos formulaires seront dcentraliss (donc
rutilisables une innit de fois), mais la cration se fera de manire beaucoup plus aise !
Bien sr, comme pour la conception de lapplication, cela deviendra rapide une fois lAPI
dveloppe.
409
CHAPITRE 22. GRER LES FORMULAIRES
Le formulaire
Conception du formulaire
Commenons dans ce chapitre par crer un premier formulaire. Un formulaire, vous le
savez, nest autre quun ensemble de champs permettant dinteragir avec le contenu du
site. Par exemple, voici notre formulaire dajout de commentaire :
1 <form action="" method="post">
2 <p>
3 <?php if (isset($erreurs) && in_array (\ Library\Entities\
Comment :: AUTEUR_INVALIDE , $erreurs)) echo 'L\'auteur est
invalide.<br />'; ?>
4 <label >Pseudo </label >
5 <input type="text" name="pseudo" value=" <?php if (isset(
$comment)) echo htmlspecialchars($comment['auteur ']); ?>
" /><br />
6
7 <?php if (isset($erreurs) && in_array (\ Library\Entities\
Comment :: CONTENU_INVALIDE , $erreurs)) echo 'Le contenu
est invalide.<br />'; ?>
8 <label >Contenu </label >
9 <textarea name="contenu" rows="7" cols="50" ><?php if (isset
($comment)) echo htmlspecialchars($comment['contenu ']);
?></textarea ><br />
10
11 <input type="submit" value="Commenter" />
12 </p>
13 </form >
Cependant, vous conviendrez quil est long et fastidieux de crer ce formulaire. De plus,
si nous voulons diter un commentaire, il va falloir le dupliquer dans lapplication ba-
ckend. Dans un premier temps, nous allons nous occuper de laspect long et fastidieux :
laissons un objet gnrer tous ces champs notre place !
Lobjet Form
Comme nous venons de le voir, un formulaire nest autre quune liste de champs.
Vous connaissez donc dj le rle de cet objet : il sera charg de reprsenter le formulaire
en possdant une liste de champs.
Commenons alors la liste des fonctionnalits de notre formulaire. Notre formulaire
contient divers champs. Nous devons donc pouvoir ajouter des champs notre for-
mulaire. Ensuite, que serait un formulaire si on ne pouvait pas lacher ? Dans notre
cas, le formulaire ne doit pas tre capable de sacher mais de gnrer tous les champs
qui lui sont attachs an que le contrleur puisse rcuprer le corps du formulaire pour
le passer la vue. Enn, notre formulaire doit possder une dernire fonctionnalit : le
capacit de dclarer si le formulaire est valide ou non en vriant que chaque champ
lest.
410
LE FORMULAIRE
Pour rsumer, nous avons donc trois fonctionnalits. Un objet Form doit tre capable :
Dajouter des champs sa liste de champs.
De gnrer le corps du formulaire.
De vrier si tous les champs sont valides.
Ainsi, au niveau des caractristiques de lobjet, nous en avons qui saute aux yeux : la
liste des champs !
Cependant, un formulaire est galement caractris par autre chose. En eet, si je vous
demande de me dire comment vous allez vrier si tous les champs sont valides, vous
sauriez comment faire ? aucun moment nous navons pass des valeurs notre formu-
laire, donc aucune vrication nest eectuer. Il faudrait donc, dans le constructeur
de notre objet Form, passer un objet contenant toutes ces valeurs. Ainsi, lors de lajout
dun champ, la mthode irait chercher la valeur correspondante dans cet objet et las-
signerait au champ (nous verrons plus tard comment la mthode sait quel attribut
de lentit correspond le champ). votre avis, quoi vont ressembler ces objets ? En
fait, vous les avez dj crs ces objets : ce sont toutes les classes lles de Entity ! Par
exemple, si vous voulez modier un commentaire, vous allez crer un objet Comment
que vous allez hydrater, puis vous crerez un objet Form en passant lobjet Comment au
constructeur.
Ainsi, voici notre classe Form schmatise (voir la gure 22.1).
Figure 22.1 Modlisation de la classe Form
Vous pouvez remarquer que la mthode add() renvoie un objet Form. En fait,
il sagit du formulaire auquel on a ajout le champ : cela permet denchaner
facilement les appels la mthode add() comme nous le verrons juste aprs.
Lobjet Field
Puisque lobjet Form est intimement li ses champs, intressons-nous la conception
de ces champs (ou elds en anglais). Vous laurez peut-tre devin : tous nos champs
seront des objets, chacun reprsentant un champ dirent (une classe reprsentera un
champ texte, une autre classe reprsentera une zone de texte, etc.). ce stade, un tilt
devrait stre produit dans votre tte : ce sont tous des champs, ils doivent donc hriter
dune mme classe reprsentant leur nature en commun, savoir une classe Field !
Commenons par cette classe Field. Quelles fonctionnalits attendons-nous de cette
411
CHAPITRE 22. GRER LES FORMULAIRES
classe ? Un objet Field doit tre capable :
De renvoyer le code HTML reprsentant le champ.
De vrier si la valeur du champ est valide.
Je pense que vous aviez ces fonctionnalits plus ou moins en tte. Cependant, il y
a encore une autre fonctionnalit que nous devons implmenter. En eet, pensez aux
classes qui hriteront de Field et qui reprsenteront chacune un type de champ. Chaque
champ a des attributs spciques. Par exemple, un champ texte (sur une ligne) possde
un attribut maxlength, tandis quune zone de texte (un textarea) possde des attributs
rows et cols. Chaque classe lle aura donc des attributs elles seules. Il serait pratique,
ds la construction de lobjet, de passer ces valeurs notre champ (par exemple, assigner
50 lattribut maxlength). Pour rsoudre ce genre de cas, nous allons procder dune
faon qui ne vous est pas inconnue : nous allons crer une mthode permettant lobjet
de shydrater ! Ainsi, notre classe Field possdera une mthode hydrate(), comme les
entits.
Voici la gure 22.2 le schma reprsentant notre classe Field lie la classe Form,
avec deux classes lles en exemple (StringField reprsentant un champ texte sur une
ligne et la classe TextField reprsentant un textarea).
Dveloppement de lAPI
La classe Form
Pour rappel, voici de quoi la classe Form est compose :
Dun attribut stockant la liste des champs.
Dun attribut stockant lentit correspondant au formulaire.
Dun constructeur rcuprant lentit et invoquant le setter correspondant.
Dune mthode permettant dajouter un champ la liste des champs.
Dune mthode permettant de gnrer le formulaire.
Dune mthode permettant de vrier si le formulaire est valide.
Voici la classe Form que vous auriez du obtenir :
1 <?php
2 namespace Library;
3
4 class Form
5 {
6 protected $entity;
7 protected $fields;
8
9 public function __construct(Entity $entity)
10 {
11 $this ->setEntity($entity);
12 }
13
14 public function add(Field $field)
15 {
412
LE FORMULAIRE
Figure 22.2 Modlisation de la classe Field
413
CHAPITRE 22. GRER LES FORMULAIRES
16 $attr = $field ->name(); // On rcupre le nom du champ.
17 $field ->setValue($this ->entity ->$attr()); // On assigne la
valeur correspondante au champ.
18
19 $this ->fields [] = $field; // On ajoute le champ pass en
argument la liste des champs.
20 return $this;
21 }
22
23 public function createView ()
24 {
25 $view = '';
26
27 // On gnre un par un les champs du formulaire.
28 foreach ($this ->fields as $field)
29 {
30 $view .= $field ->buildWidget ().'<br />';
31 }
32
33 return $view;
34 }
35
36 public function isValid ()
37 {
38 $valid = true;
39
40 // On vrifie que tous les champs sont valides.
41 foreach ($this ->fields as $field)
42 {
43 if (!$field ->isValid ())
44 {
45 $valid = false;
46 }
47 }
48
49 return $valid;
50 }
51
52 public function entity ()
53 {
54 return $this ->entity;
55 }
56
57 public function setEntity(Entity $entity)
58 {
59 $this ->entity = $entity;
60 }
61 }
414
LE FORMULAIRE
La classe Field et ses lles
Voici un petit rappel sur la composition de la classe Field. Cette classe doit tre
compose :
Dun attribut stockant le message derreur associ au champ.
Dun attribut stockant le label du champ.
Dun attribut stockant le nom du champ.
Dun attribut stockant la valeur du champ.
Dun constructeur demandant la liste des attributs avec leur valeur an dhydrater
lobjet.
Dune mthode (abstraite) charge de renvoyer le code HTML du champ.
Dune mthode permettant de savoir si le champ est valide ou non.
Les classes lles, quant elles, nimplmenteront que la mthode abstraite. Si elles pos-
sdent des attributs spciques (comme lattribut maxlength pour la classe StringField),
alors elles devront implmenter les mutateurs correspondant (comme vous le verrez plus
tard, ce nest pas ncessaire dimplmenter les accesseurs).
Voici les trois classes que vous auriez du obtenir (la classe Field avec deux classes lles
en exemple, StringField et TextField) :
1 <?php
2 namespace Library;
3
4 abstract class Field
5 {
6 protected $errorMessage;
7 protected $label;
8 protected $name;
9 protected $value;
10
11 public function __construct(array $options = array())
12 {
13 if (!empty($options))
14 {
15 $this ->hydrate($options);
16 }
17 }
18
19 abstract public function buildWidget ();
20
21 public function hydrate($options)
22 {
23 foreach ($options as $type => $value)
24 {
25 $method = 'set'.ucfirst($type);
26
27 if (is_callable(array($this , $method)))
28 {
29 $this ->$method($value);
415
CHAPITRE 22. GRER LES FORMULAIRES
30 }
31 }
32 }
33
34 public function isValid ()
35 {
36 // On crira cette mthode plus tard.
37 }
38
39 public function label ()
40 {
41 return $this ->label;
42 }
43
44 public function name()
45 {
46 return $this ->name;
47 }
48
49 public function value ()
50 {
51 return $this ->value;
52 }
53
54 public function setLabel($label)
55 {
56 if (is_string($label))
57 {
58 $this ->label = $label;
59 }
60 }
61
62 public function setName($name)
63 {
64 if (is_string($name))
65 {
66 $this ->name = $name;
67 }
68 }
69
70 public function setValue($value)
71 {
72 if (is_string($value))
73 {
74 $this ->value = $value;
75 }
76 }
77 }
1 <?php
416
LE FORMULAIRE
2 namespace Library;
3
4 class StringField extends Field
5 {
6 protected $maxLength;
7
8 public function buildWidget ()
9 {
10 $widget = '';
11
12 if (!empty($this ->errorMessage))
13 {
14 $widget .= $this ->errorMessage.'<br />';
15 }
16
17 $widget .= '<label >'.$this ->label.'</label ><input type="
text" name="'.$this ->name.'"';
18
19 if (!empty($this ->value))
20 {
21 $widget .= ' value="'.htmlspecialchars($this ->value).'"';
22 }
23
24 if (!empty($this ->maxLength))
25 {
26 $widget .= ' maxlength ="'.$this ->maxLength.'"';
27 }
28
29 return $widget .= ' />';
30 }
31
32 public function setMaxLength($maxLength)
33 {
34 $maxLength = (int) $maxLength;
35
36 if ($maxLength > 0)
37 {
38 $this ->maxLength = $maxLength;
39 }
40 else
41 {
42 throw new \RuntimeException('La longueur maximale doit
tre un nombre suprieur 0');
43 }
44 }
45 }
1 <?php
2 namespace Library;
3
417
CHAPITRE 22. GRER LES FORMULAIRES
4 class TextField extends Field
5 {
6 protected $cols;
7 protected $rows;
8
9 public function buildWidget ()
10 {
11 $widget = '';
12
13 if (!empty($this ->errorMessage))
14 {
15 $widget .= $this ->errorMessage.'<br />';
16 }
17
18 $widget .= '<label >'.$this ->label.'</label ><textarea name="
'.$this ->name.'"';
19
20 if (!empty($this ->cols))
21 {
22 $widget .= ' cols="'.$this ->cols.'"';
23 }
24
25 if (!empty($this ->rows))
26 {
27 $widget .= ' rows="'.$this ->rows.'"';
28 }
29
30 $widget .= '>';
31
32 if (!empty($this ->value))
33 {
34 $widget .= htmlspecialchars($this ->value);
35 }
36
37 return $widget.'</textarea >';
38 }
39
40 public function setCols($cols)
41 {
42 $cols = (int) $cols;
43
44 if ($cols > 0)
45 {
46 $this ->cols = $cols;
47 }
48 }
49
50 public function setRows($rows)
51 {
52 $rows = (int) $rows;
418
LE FORMULAIRE
53
54 if ($rows > 0)
55 {
56 $this ->rows = $rows;
57 }
58 }
59 }
Testons nos nouvelles classes
Testons ds maintenant nos classes. Dans notre contrleur de news du frontend, nous
allons modier laction charge dajouter un commentaire. Crons notre formulaire avec
nos nouvelles classes :
1 <?php
2 namespace Applications\Frontend\Modules\News;
3
4 class NewsController extends \Library\BackController
5 {
6 // ...
7
8 public function executeInsertComment (\ Library\HTTPRequest
$request)
9 {
10 // Si le formulaire a t envoy, on cre le commentaire
avec les valeurs du formulaire.
11 if ($request ->method () == 'POST')
12 {
13 $comment = new \Library\Entities\Comment(array(
14 'news' => $request ->getData('news'),
15 'auteur ' => $request ->postData('auteur '),
16 'contenu ' => $request ->postData('contenu ')
17 ));
18 }
19 else
20 {
21 $comment = new \Library\Entities\Comment;
22 }
23
24 $form = new Form($comment);
25
26 $form ->add(new \Library\StringField(array(
27 'label' => 'Auteur ',
28 'name' => 'auteur ',
29 'maxLength ' => 50),
30 )))
31 ->add(new \Library\TextField(array(
32 'label' => 'Contenu ',
33 'name' => 'contenu ',
419
CHAPITRE 22. GRER LES FORMULAIRES
34 'rows' => 7,
35 'cols' => 50 ,
36 )));
37
38 if ($form ->isValid ())
39 {
40 // On enregistre le commentaire
41 }
42
43 $this ->page ->addVar('comment ', $comment);
44 $this ->page ->addVar('form', $form ->createView ()); // On
passe le formulaire gnr la vue.
45 $this ->page ->addVar('title', 'Ajout d\'un commentaire ');
46 }
47
48 // ...
49 }
1 <h2>Ajouter un commentaire </h2 >
2 <form action="" method="post">
3 <p>
4 <?php echo $form; ?>
5
6 <input type="submit" value="Commenter" />
7 </p>
8 </form >
Cependant, avouez que ce nest pas pratique davoir ceci en plein milieu de notre
contrleur. De plus, si nous avons besoin de crer ce formulaire un autre endroit,
nous devrons copier/coller tous ces appels la mthode add() et recrer tous les
champs. Niveau duplication de code, nous sommes servi ! Nous rsoudrons ce problme
dans la suite du chapitre. Mais avant cela, intressons-nous la validation du formu-
laire. En eet, le contenu de la mthode isValid() est rest vide : faisons appel aux
validateurs !
Les validateurs
Un validateur, comme son nom lindique, est charg de valider une donne. Mais atten-
tion : un validateur ne peut valider quune contrainte. Par exemple, si vous voulez
vrier que votre valeur nest pas nulle et quelle ne dpasse pas les cinquante carac-
tres, alors vous aurez besoin de deux validateurs : le premier vriera que la valeur
nest pas nulle, et le second vriera que la chaine de caractres ne dpassera pas les
cinquante caractres.
L aussi, vous devriez savoir ce qui vous attend au niveau des classes : nous aurons une
classe de base (Validator) et une innit de classes lles (dans le cas prcdent, on
peut imaginer les classes NotNullValidator et MaxLengthValidator). Attaquons-les
ds maintenant !
420
LES VALIDATEURS
Conception des classes
La classe Validator
Notre classe de base, Validator, sera charge, comme nous lavons dit, de valider une
donne. Et cest tout : un validateur ne sert rien dautre que valider une donne. Au
niveau des caractristiques, il ny en a dans ce cas galement, une seule : le message
derreur que le validateur doit pouvoir renvoyer si la valeur passe nest pas valide.
Voici donc notre classe schmatise (voir la gure 22.3).
Figure 22.3 Modlisation de notre classe Validator
Les classes lles
Les classes lles sont trs simples. Commenons par la plus facile : NotNullValidator.
Celle-ci, comme toute classe lle, sera charge dimplmenter la mthode isValid($value).
Et cest tout ! La seconde classe, MaxLengthValidator, implmente elle aussi cette
mthode. Cependant, il faut quelle connaisse le nombre de caractres maximal que la
chane doit avoir ! Pour cela, cette classe implmentera un constructeur demandant ce
nombre en paramtre, et assignera cette valeur lattribut correspondant.
Ainsi, voici nos deux classes lles hritant de Validator (voir la gure 22.4).
Figure 22.4 Modlisation des classes MaxLengthValidator et NotNullValidator
421
CHAPITRE 22. GRER LES FORMULAIRES
Dveloppement des classes
La classe Validator
Cette classe (comme les classes lles) est assez simple dvelopper. En eet, il ny a
que laccesseur et le mutateur du message derreur implmenter, avec un constructeur
demandant ledit message derreur. La mthode isValid(), quant elle, est abstraite,
donc rien crire de ce ct-l !
1 <?php
2 namespace Library;
3
4 abstract class Validator
5 {
6 protected $errorMessage;
7
8 public function __construct($errorMessage)
9 {
10 $this ->setErrorMessage($errorMessage);
11 }
12
13 abstract public function isValid($value);
14
15 public function setErrorMessage($errorMessage)
16 {
17 if (is_string($errorMessage))
18 {
19 $this ->errorMessage = $errorMessage;
20 }
21 }
22
23 public function errorMessage ()
24 {
25 return $this ->errorMessage;
26 }
27 }
Les classes lles
Comme la prcdente, les classes lles sont trs simples concevoir. Jespre que vous
y tes parvenus !
1 <?php
2 namespace Library;
3
4 class NotNullValidator extends Validator
5 {
6 public function isValid($value)
7 {
422
LES VALIDATEURS
8 return $value != '';
9 }
10 }
1 <?php
2 namespace Library;
3
4 class MaxLengthValidator extends Validator
5 {
6 protected $maxLength;
7
8 public function __construct($errorMessage , $maxLength)
9 {
10 parent :: __construct($errorMessage);
11
12 $this ->setMaxLength($maxLength);
13 }
14
15 public function isValid($value)
16 {
17 return strlen($value) <= $this ->maxLength;
18 }
19
20 public function setMaxLength($maxLength)
21 {
22 $maxLength = (int) $maxLength;
23
24 if ($maxLength > 0)
25 {
26 $this ->maxLength = $maxLength;
27 }
28 else
29 {
30 throw new \RuntimeException('La longueur maximale doit
tre un nombre suprieur 0');
31 }
32 }
33 }
Modication de la classe Field
Comme nous lavions vu, pour savoir si un champ est valide, il lui faut des validateurs.
Il va donc falloir passer, dans le constructeur de lobjet Field cr, la liste des vali-
dateurs que lon veut imposer au champ. Dans le cas du champ auteur par exemple,
nous lui passerons les deux validateurs : nous voulons la fois que le champ ne soit pas
vide et que la valeur ne dpasse pas les cinquante caractres. La cration du formulaire
ressemblerait donc ceci :
1 <?php
423
CHAPITRE 22. GRER LES FORMULAIRES
2 // $form reprsente le formulaire que l'on souhaite crer.
3 // Ici , on souhaite lui ajouter le champ auteur .
4 $form ->add(new \Library\StringField(array(
5 'label' => 'Auteur ',
6 'name' => 'auteur ',
7 'maxLength ' => 50 ,
8 'validators ' => array(
9 new \Library\MaxLengthValidator('L\'auteur spcifi est
trop long (50 caractres maximum)', 50),
10 new \Library\NotNullValidator('Merci de spcifier l\'auteur
du commentaire '),
11 )
12 )));
De cette faon, quelques modications au niveau de notre classe Field simposent. En
eet, il va falloir crer un attribut $validators, ainsi que laccesseur et le mutateur
correspondant. De la sorte, notre mthode hydrate() assignera automatiquement les
validateurs passs au constructeur lattribut $validators. Je vous laisse faire cela.
Vient maintenant limplmentation de la mthode isValid(). Cette mthode doit par-
courir tous les validateurs et invoquer la mthode isValid($value) sur ces validateurs
an de voir si la valeur passe au travers du let de tous les validateurs. De cette faon,
nous sommes srs que toutes les contraintes ont t respectes ! Si un validateur renvoie
une rponse ngative lorsquon lui demande si la valeur est valide, alors on devra lui
demander le message derreur qui lui a t assign et lassigner notre tour lattribut
correspondant. Ainsi, voici la nouvelle classe Field :
1 <?php
2 namespace Library;
3
4 abstract class Field
5 {
6 protected $errorMessage;
7 protected $label;
8 protected $name;
9 protected $validators = array ();
10 protected $value;
11
12 public function __construct(array $options = array())
13 {
14 if (!empty($options))
15 {
16 $this ->hydrate($options);
17 }
18 }
19
20 abstract public function buildWidget ();
21
22 public function hydrate($options)
23 {
24 foreach ($options as $type => $value)
424
LES VALIDATEURS
25 {
26 $method = 'set'.ucfirst($type);
27
28 if (is_callable(array($this , $method)))
29 {
30 $this ->$method($value);
31 }
32 }
33 }
34
35 public function isValid ()
36 {
37 foreach ($this ->validators as $validator)
38 {
39 if (!$validator ->isValid($this ->value))
40 {
41 $this ->errorMessage = $validator ->errorMessage ();
42 return false;
43 }
44 }
45
46 return true;
47 }
48
49 public function label ()
50 {
51 return $this ->label;
52 }
53
54 public function length ()
55 {
56 return $this ->length;
57 }
58
59 public function name()
60 {
61 return $this ->name;
62 }
63
64 public function validators ()
65 {
66 return $this ->validators;
67 }
68
69 public function value ()
70 {
71 return $this ->value;
72 }
73
74 public function setLabel($label)
425
CHAPITRE 22. GRER LES FORMULAIRES
75 {
76 if (is_string($label))
77 {
78 $this ->label = $label;
79 }
80 }
81
82 public function setLength($length)
83 {
84 $length = (int) $length;
85
86 if ($length > 0)
87 {
88 $this ->length = $length;
89 }
90 }
91
92 public function setName($name)
93 {
94 if (is_string($name))
95 {
96 $this ->name = $name;
97 }
98 }
99
100 public function setValidators(array $validators)
101 {
102 foreach ($validators as $validator)
103 {
104 if ($validator instanceof Validator && !in_array(
$validator , $this ->validators))
105 {
106 $this ->validators [] = $validator;
107 }
108 }
109 }
110
111 public function setValue($value)
112 {
113 if (is_string($value))
114 {
115 $this ->value = $value;
116 }
117 }
118 }
Vous pouvez apercevoir lutilisation de loprateur instanceof dans le code.
Pour en savoir plus ce sujet, je vous invite aller lire le chapitre ddi cet
oprateur.
426
LE CONSTRUCTEUR DE FORMULAIRES
Le constructeur de formulaires
Comme nous lavons vu, crer le formulaire au sein du contrleur prsente deux in-
convnients. Premirement, cela encombre le contrleur. Imaginez que vous ayez une
dizaine de champs, cela deviendrait norme ! Le contrleur doit tre clair, et la cration
du formulaire devrait donc se faire autre part. Deuximement, il y a le problme de
duplication de code : si vous voulez utiliser ce formulaire dans un autre contrleur,
vous devrez copier/coller tout le code responsable de la cration du formulaire. Pas
trs exible vous en conviendrez ! Pour cela, nous allons donc crer des constructeurs
de formulaire. Il y aura par consquent autant de constructeurs que de formulaires
dirents.
Conception des classes
La classe FormBuilder
La classe FormBuilder a un rle bien prcis : elle est charge de construire un formu-
laire. PAinsi, il ny a quune seule fonctionnalit implmenter. . . celle de construire le
formulaire ! Mais, pour ce faire, encore faudrait-il avoir un objet Form. Nous le crerons
donc dans le constructeur et nous lassignerons lattribut correspondant.
Nous avons donc :
Une mthode abstraite charge de construire le formulaire.
Un attribut stockant le formulaire.
Laccesseur et le mutateur correspondant.
Voici notre classe schmatise (voir la gure 22.5).
Figure 22.5 Modlisation de la classe FormBuilder
Les classes lles
Un constructeur de base cest bien beau, mais sans classe lle, dicile de construire
grand-chose. Je vous propose donc de crer deux constructeurs de formulaire : un
constructeur de formulaire de commentaires, et un constructeur de formulaire de news.
Nous aurons notre classe FormBuilder dont hriteront deux classes, CommentFormBuilder
et NewsFormBuilder (voir la gure 22.6).
427
CHAPITRE 22. GRER LES FORMULAIRES
Figure 22.6 Modlisation des classes CommentFormBuilder et NewsFormBuilder
Dveloppement des classes
La classe FormBuilder
Cette classe est assez simple crer, jespre que vous y tes parvenus !
1 <?php
2 namespace Library;
3
4 abstract class FormBuilder
5 {
6 protected $form;
7
8 public function __construct(Entity $entity)
9 {
10 $this ->setForm(new Form($entity));
11 }
12
13 abstract public function build();
14
15 public function setForm(Form $form)
16 {
17 $this ->form = $form;
18 }
19
20 public function form()
21 {
22 return $this ->form;
23 }
24 }
Les classes lles
Les classes lles sont simples crer. En eet, il ny a que la mthode build()
implmenter, en ayant pour simple contenu dappeler successivement les mthodes
add() sur notre formulaire. Pour lemplacement des chiers stockant les classes, je
428
LE CONSTRUCTEUR DE FORMULAIRES
vous propose de les placer dans le dossier /Library/FormBuilder.
1 <?php
2 namespace Library\FormBuilder;
3
4 class CommentFormBuilder extends \Library\FormBuilder
5 {
6 public function build ()
7 {
8 $this ->form ->add(new \Library\StringField(array(
9 'label' => 'Auteur ',
10 'name' => 'auteur ',
11 'maxLength ' => 50 ,
12 'validators ' => array(
13 new \Library\MaxLengthValidator('L\'auteur spcifi
est trop long (50 caractres maximum)', 50),
14 new \Library\NotNullValidator('Merci de spcifier l\'
auteur du commentaire '),
15 ),
16 )))
17 ->add(new \Library\TextField(array(
18 'label' => 'Contenu ',
19 'name' => 'contenu ',
20 'rows' => 7,
21 'cols' => 50 ,
22 'validators ' => array(
23 new \Library\NotNullValidator('Merci de spcifier
votre commentaire '),
24 ),
25 )));
26 }
27 }
1 <?php
2 namespace Library\FormBuilder;
3
4 class NewsFormBuilder extends \Library\FormBuilder
5 {
6 public function build ()
7 {
8 $this ->form ->add(new \Library\StringField(array(
9 'label' => 'Auteur ',
10 'name' => 'auteur ',
11 'maxLength ' => 20 ,
12 'validators ' => array(
13 new \Library\MaxLengthValidator('L\'auteur spcifi
est trop long (20 caractres maximum)', 20),
14 new \Library\NotNullValidator('Merci de spcifier l\'
auteur de la news'),
15 ),
16 )))
429
CHAPITRE 22. GRER LES FORMULAIRES
17 ->add(new \Library\StringField(array(
18 'label' => 'Titre',
19 'name' => 'titre',
20 'maxLength ' => 100 ,
21 'validators ' => array(
22 new \Library\MaxLengthValidator('Le titre spcifi
est trop long (100 caractres maximum)', 100),
23 new \Library\NotNullValidator('Merci de spcifier le
titre de la news'),
24 ),
25 )))
26 ->add(new \Library\TextField(array(
27 'label' => 'Contenu ',
28 'name' => 'contenu ',
29 'rows' => 8,
30 'cols' => 60 ,
31 'validators ' => array(
32 new \Library\NotNullValidator('Merci de spcifier le
contenu de la news'),
33 ),
34 )));
35 }
36 }
Modication des contrleurs
Lajout de commentaire (frontend)
Eectuons des premires modications, en commenant par le formulaire dajout de
commentaire dans le frontend. En utilisant nos classes, voici les instructions que nous
devons excuter :
Si la requte est de type POST (formulaire soumis), il faut crer un nouveau com-
mentaire en le remplissant avec les donnes envoyes, sinon on cre un nouveau
commentaire.
On instancie notre constructeur de formulaire en lui passant le commentaire en ar-
gument.
On invoque la mthode de construction du formulaire.
Si le formulaire est valide, on enregistre le commentaire en BDD.
On passe le formulaire gnr la vue.
Voil ce que a donne :
1 <?php
2 namespace Applications\Frontend\Modules\News;
3
4 class NewsController extends \Library\BackController
5 {
6 // ...
430
LE CONSTRUCTEUR DE FORMULAIRES
7
8 public function executeInsertComment (\ Library\HTTPRequest
$request)
9 {
10 // Si le formulaire a t envoy.
11 if ($request ->method () == 'POST')
12 {
13 $comment = new \Library\Entities\Comment(array(
14 'news' => $request ->getData('news'),
15 'auteur ' => $request ->postData('auteur '),
16 'contenu ' => $request ->postData('contenu ')
17 ));
18 }
19 else
20 {
21 $comment = new \Library\Entities\Comment;
22 }
23
24 $formBuilder = new \Library\FormBuilder\CommentFormBuilder(
$comment);
25 $formBuilder ->build();
26
27 $form = $formBuilder ->form();
28
29 if($request ->method () == 'POST' && $form ->isValid ())
30 {
31 $this ->managers ->getManagerOf('Comments ')->save($comment)
;
32 $this ->app ->user()->setFlash('Le commentaire a bien t
ajout, merci !');
33 $this ->app ->httpResponse ()->redirect('news -'.$request ->
getData('news').'.html');
34 }
35
36 $this ->page ->addVar('comment ', $comment);
37 $this ->page ->addVar('form', $form ->createView ());
38 $this ->page ->addVar('title', 'Ajout d\'un commentaire ');
39 }
40
41 // ...
42 }
La modication de commentaire, lajout et la modication de news (ba-
ckend)
Normalement, vous devriez tre capables, grce lexemple prcdent, de parvenir
crer ces trois autres formulaires. Voici le nouveau contrleur :
1 <?php
431
CHAPITRE 22. GRER LES FORMULAIRES
2 namespace Applications\Backend\Modules\News;
3
4 class NewsController extends \Library\BackController
5 {
6 // ...
7
8 public function executeInsert (\ Library\HTTPRequest $request)
9 {
10 $this ->processForm($request);
11
12 $this ->page ->addVar('title', 'Ajout d\'une news');
13 }
14
15 public function executeUpdate (\ Library\HTTPRequest $request)
16 {
17 $this ->processForm($request);
18
19 $this ->page ->addVar('title', 'Modification d\'une news');
20 }
21
22 public function executeUpdateComment (\ Library\HTTPRequest
$request)
23 {
24 $this ->page ->addVar('title', 'Modification d\'un
commentaire ');
25
26 if ($request ->method () == 'POST')
27 {
28 $comment = new \Library\Entities\Comment(array(
29 'id' => $request ->getData('id'),
30 'auteur ' => $request ->postData('auteur '),
31 'contenu ' => $request ->postData('contenu ')
32 ));
33 }
34 else
35 {
36 $comment = $this ->managers ->getManagerOf('Comments ')->get
($request ->getData('id'));
37 }
38
39 $formBuilder = new \Library\FormBuilder\CommentFormBuilder(
$comment);
40 $formBuilder ->build();
41
42 $form = $formBuilder ->form();
43
44 if ($request ->method () == 'POST' && $form ->isValid ())
45 {
46 $this ->managers ->getManagerOf('Comments ')->save($comment)
;
432
LE CONSTRUCTEUR DE FORMULAIRES
47 $this ->app ->user()->setFlash('Le commentaire a bien t
modifi');
48 $this ->app ->httpResponse ()->redirect('/admin/');
49 }
50
51 $this ->page ->addVar('form', $form ->createView ());
52 }
53
54 public function processForm (\ Library\HTTPRequest $request)
55 {
56 if ($request ->method () == 'POST')
57 {
58 $news = new \Library\Entities\News(
59 array(
60 'auteur ' => $request ->postData('auteur '),
61 'titre' => $request ->postData('titre'),
62 'contenu ' => $request ->postData('contenu ')
63 )
64 );
65
66 if ($request ->getExists('id'))
67 {
68 $news ->setId($request ->getData('id'));
69 }
70 }
71 else
72 {
73 // L'identifiant de la news est transmis si on veut la
modifier.
74 if ($request ->getExists('id'))
75 {
76 $news = $this ->managers ->getManagerOf('News')->
getUnique($request ->getData('id'));
77 }
78 else
79 {
80 $news = new \Library\Entities\News;
81 }
82 }
83
84 $formBuilder = new \Library\FormBuilder\NewsFormBuilder(
$news);
85 $formBuilder ->build();
86
87 $form = $formBuilder ->form();
88
89 if ($request ->method () == 'POST' && $form ->isValid ())
90 {
91 $this ->managers ->getManagerOf('News')->save($news);
433
CHAPITRE 22. GRER LES FORMULAIRES
92 $this ->app ->user()->setFlash($news ->isNew() ? 'La news a
bien t ajoute !' : 'La news a bien t modifie !')
;
93 $this ->app ->httpResponse ()->redirect('/admin/');
94 }
95
96 $this ->page ->addVar('form', $form ->createView ());
97 }
98 }
Le gestionnaire de formulaires
Terminons ce chapitre en amliorant encore notre API permettant la cration de for-
mulaire. Je voudrais attirer votre attention sur ce petit passage, que lon retrouve
chaque fois (que ce soit pour ajouter ou modier une news ou un commentaire) :
1 <?php
2 // Nous sommes ici au sein d'un contrleur
3 if($request ->method () == 'POST' && $form ->isValid ())
4 {
5 $this ->managers ->getManagerOf('Manager ')->save($comment);
6 // ...
7 }
Bien que rduit, ce bout de code est lui aussi dupliqu. De plus, si lon veut vraiment
externaliser la gestion du formulaire, alors il va falloir le sortir du contrleur. Ainsi, il
ne restera plus dopration de traitement dans le contrleur. On sparera donc bien les
rles : le contrleur naura plus rchir sur le formulaire quil traite. En eet, il ne
fera que demander au constructeur de formulaire de construire le formulaire quil veut,
puis demandera au gestionnaire de formulaire de soccuper de lui sil a t envoy. On
ne se souciera donc plus de laspect interne du formulaire !
Conception du gestionnaire de formulaire
Comme nous venons de le voir, le gestionnaire de formulaire est charg de traiter le for-
mulaire une fois quil a t envoy. Nous avons donc dores et dj une fonctionnalit de
notre classe : celle de traiter le formulaire. Du ct des caractristiques, penchons-nous
du ct des lments dont notre gestionnaire a besoin pour fonctionner. Le premier
lment me parat vident : comment soccuper dun formulaire si on ny a pas ac-
cs ? Ce premier lment est donc bien entendu le formulaire dont il est question. Le
deuxime lment, lui, est aussi vident : comment enregistrer lentit correspondant
au formulaire si on na pas le manager correspondant ? Le deuxime lment est donc
le manager correspondant lentit. Enn, le troisime lment est un peu plus subtil,
et il faut rchir au contenu de la mthode qui va traiter le formulaire. Cette mthode
devra savoir si le formulaire a t envoy pour pouvoir le traiter (si rien na t envoy,
il ny a aucune raison de traiter quoi que ce soit). Ainsi, pour savoir si le formulaire a
434
LE GESTIONNAIRE DE FORMULAIRES
t envoy, il faut que notre gestionnaire de formulaire ait accs la requte du client
an de connaitre le type de la requte (GET ou POST). Ces trois lments devront
tre passs au constructeur de notre objet.
Schmatiquement, voici notre gestionnaire de formulaire (voir la gure 22.7).
Figure 22.7 Modlisation de la classe FormHandler
Dveloppement du gestionnaire de formulaire
Voici le rsultat que vous auriez du obtenir :
1 <?php
2 namespace Library;
3
4 class FormHandler
5 {
6 protected $form;
7 protected $manager;
8 protected $request;
9
10 public function __construct (\ Library\Form $form , \Library\
Manager $manager , \Library\HTTPRequest $request)
11 {
12 $this ->setForm($form);
13 $this ->setManager($manager);
14 $this ->setRequest($request);
15 }
16
17 public function process ()
18 {
19 if($this ->request ->method () == 'POST' && $this ->form ->
isValid ())
20 {
21 $this ->manager ->save($this ->form ->entity ());
22
23 return true;
24 }
25
26 return false;
27 }
435
CHAPITRE 22. GRER LES FORMULAIRES
28
29 public function setForm (\ Library\Form $form)
30 {
31 $this ->form = $form;
32 }
33
34 public function setManager (\ Library\Manager $manager)
35 {
36 $this ->manager = $manager;
37 }
38
39 public function setRequest (\ Library\HTTPRequest $request)
40 {
41 $this ->request = $request;
42 }
43 }
Modication des contrleurs
Ici, la modication est trs simple. En eet, nous avons juste dcentralis ce bout de
code :
1 <?php
2 if($request ->method () == 'POST' && $form ->isValid ())
3 {
4 $this ->managers ->getManagerOf('Manager ')->save($comment);
5 // Autres oprations (affichage d'un message informatif ,
redirection , etc.).
6 }
Il sut donc de remplacer ce code par la simple invocation de la mthode process()
sur notre objet FormHandler :
1 <?php
2 // On rcupre le gestionnaire de formulaire (le paramtre de
getManagerOf () est bien entendu remplacer).
3 $formHandler = new \Library\FormHandler($form , $this ->managers
->getManagerOf('Comments '), $request);
4
5 if ($formHandler ->process ())
6 {
7 // Ici ne rsident plus que les oprations effectuer une
fois l'entit du formulaire enregistre
8 // (affichage d'un message informatif , redirection , etc.).
9 }
Je vous fais conance pour mettre jour vos contrleurs comme il se doit !
436
Quatrime partie
Annexes
437
Chapitre 23
Loprateur instanceof
Dicult :
V
ous tes-vous dj demand sil tait possible de savoir si un objet tait une instance
de telle classe ? Si vous vous tes dj pos cette question, vous navez normalement
pas trouv de rponse vraiment claire. Un moyen simple de vrier une telle chose
est dutiliser loprateur instanceof. Ce sera un court chapitre car cette notion nest pas
bien dicile. Il faut juste possder quelques pr-rquis.
En voici la liste :
Bien matriser les notions de classe, dobjet et dinstance .
Bien matriser le concept de lhritage (si vous ne matrisez pas bien la rsolution statique
la vole ce nest pas bien important).
Savoir ce quest une interface et savoir sen servir.
439
CHAPITRE 23. LOPRATEUR INSTANCEOF
Prsentation de loprateur
Loprateur instanceof permet de vrier si tel objet est une instance de telle classe.
Cest un oprateur qui sutilise dans une condition. Ainsi, on pourra crer des conditions
comme si $monObjet est une instance de MaClasse, alors. . . .
Maintenant voyons comment construire notre condition. gauche de notre opra-
teur,nous allons y placer notre objet. droite de notre oprateur, nous allons placer,
comme vous vous en doutez srement, le nom de la classe.
Exemple :
1 <?php
2 class A { }
3 class B { }
4
5 $monObjet = new A;
6
7 if ($monObjet instanceof A) // Si $monObjet est une instance de
A.
8 {
9 echo '$monObjet est une instance de A';
10 }
11 else
12 {
13 echo '$monObjet n\'est pas une instance de A';
14 }
15
16 if ($monObjet instanceof B) // Si $monObjet est une instance de
B.
17 {
18 echo '$monObjet est une instance de B';
19 }
20 else
21 {
22 echo '$monObjet n\'est pas une instance de B';
23 }
24 ?>
Bref, je pense que vous avez compris le principe.
Si votre version de PHP est ultrieure la version 5.1, alors aucune erreur
fatale ne sera gnre si vous utilisez loprateur instanceof en spciant
une classe qui na pas t dclare. La condition renverra tout simplement
false.
Il y a cependant plusieurs faons de procder et quelques astuces (cest dailleurs pour
toutes les prsenter que jai cr ce chapitre).
Parmi ces mthodes, il y en a une qui consiste placer le nom de la classe pour laquelle
on veut vrier que tel objet est une instance dans une variable sous forme de chane
440
PRSENTATION DE LOPRATEUR
de caractres. Exemple :
1 <?php
2 class A { }
3 class B { }
4
5 $monObjet = new A;
6
7 $classeA = 'A';
8 $classeB = 'B';
9
10 if ($monObjet instanceof $classeA)
11 {
12 echo '$monObjet est une instance de ', $classeA;
13 }
14 else
15 {
16 echo '$monObjet n\'est pas une instance de ', $classeA;
17 }
18
19 if ($monObjet instanceof $classeB)
20 {
21 echo '$monObjet est une instance de ', $classeB;
22 }
23 else
24 {
25 echo '$monObjet n\'est pas une instance de ', $classeB;
26 }
27 ?>
Attention ! Vous ne pouvez spcier le nom de la classe entre apostrophes ou
guillemets directement dans la condition ! Vous devez obligatoirement passer
par une variable. Si vous le faites directement, vous obtiendrez une belle erreur
danalyse.
Une autre faon dutiliser cet oprateur est de spcier un autre objet la place du
nom de la classe. La condition renverra true si les deux objets sont des instances de
la mme classe. Exemple :
1 <?php
2 class A { }
3 class B { }
4
5 $a = new A;
6 $b = new A;
7 $c = new B;
8
9 if ($a instanceof $b)
10 {
11 echo '$a et $b sont des instances de la mme classe ';
441
CHAPITRE 23. LOPRATEUR INSTANCEOF
12 }
13 else
14 {
15 echo '$a et $b ne sont pas des instances de la mme classe ';
16 }
17
18 if ($a instanceof $c)
19 {
20 echo '$a et $c sont des instances de la mme classe ';
21 }
22 else
23 {
24 echo '$a et $c ne sont pas des instances de la mme classe ';
25 }
26 ?>
Et voil. Vous connaissez les trois mthodes possibles pour utiliser cet oprateur. Pour-
tant, il existe encore quelques eets que peut produire instanceof. Poursuivons donc
ce chapitre tranquillement.
instanceof et lhritage
Lhritage est de retour ! En eet, instanceof a un comportement bien particulier avec
les classes qui hritent entre elles. Voici ces eets.
Vous vous souvenez sans doute (enn jespre :- ) de la premire faon dutiliser
loprateur. Voici une rvlation : la condition renvoie true si la classe spcie est une
classe parente de la classe instancie par lobjet spci. Exemple :
1 <?php
2 class A { }
3 class B extends A { }
4 class C extends B { }
5
6 $b = new B;
7
8 if ($b instanceof A)
9 {
10 echo '$b est une instance de A ou $b instancie une classe qui
est une fille de A';
11 }
12 else
13 {
14 echo '$b n\'est pas une instance de A et $b instancie une
classe qui n\'est pas une fille de A';
15 }
16
17 if ($b instanceof C)
18 {
442
INSTANCEOF ET LES INTERFACES
19 echo '$b est une instance de C ou $b instancie une classe qui
est une fille de C';
20 }
21 else
22 {
23 echo '$b n\'est pas une instance de C et $b instancie une
classe qui n\'est pas une fille de C';
24 }
25 ?>
Voil, jespre que vous avez compris le principe car celui-ci est le mme avec les
deuxime et troisime mthodes.
Nous allons donc maintenant terminer ce chapitre avec une dernire partie concernant
les ractions de loprateur avec les interfaces. Ce sera un mlange des deux premires
parties, donc si vous tes perdus, relisez bien tout (eh oui, jespre que vous navez pas
oubli lhritage entre interfaces :- ).
instanceof et les interfaces
Voyons maintenant les eets produits par loprateur avec les interfaces.
Hein ? Comment a ? Je comprends pas. . . Comment peut-on vrier quun
objet soit une instance dune interface sachant que cest impossible ?
Comme vous le dites si bien, il est impossible de crer une instance dune interface
(au mme titre que de crer une instance dune classe abstraite, ce quest peu prs
une interface). Loprateur va donc renvoyer true si tel objet instancie une classe
implmentant telle interface.
Voici un exemple :
1 <?php
2 interface iA { }
3 class A implements iA { }
4 class B { }
5
6 $a = new A;
7 $b = new B;
8
9 if ($a instanceof iA)
10 {
11 echo 'Si iA est une classe , alors $a est une instance de iA
ou $a instancie une classe qui est une fille de iA. Sinon ,
$a instancie une classe qui implmente iA.';
12 }
13 else
14 {
443
CHAPITRE 23. LOPRATEUR INSTANCEOF
15 echo 'Si iA est une classe , alors $a n\'est pas une instance
de iA et $a n\'instancie aucune classe qui est une fille
de iA. Sinon , $a instancie une classe qui n\'implmente
pas iA.';
16 }
17
18 if ($b instanceof iA)
19 {
20 echo 'Si iA est une classe , alors $b est une instance de iA
ou $b instancie une classe qui est une fille de iA. Sinon ,
$b instancie une classe qui implmente iA.';
21 }
22 else
23 {
24 echo 'Si iA est une classe , alors $b n\'est pas une instance
de iA et $b n\'instancie aucune classe qui est une fille
de iA. Sinon , $b instancie une classe qui n\'implmente
pas iA.';
25 }
26 ?>
Ce code se passe de commentaires, les valeurs aches dtaillant assez bien je pense.
Aprs avoir vu lutilisation de loprateur avec les interfaces, nous allons voir comment
il ragit lorsquon lui passe en paramtre une interface qui est hrite par une autre
interface qui est implmente par une classe qui est instancie. Vous voyez peu prs
quoi je fais srfrence ? Je vais procder en PHP au cas o vous nayez pas tout suivi.
1 <?php
2 interface iParent { }
3 interface iFille extends iParent { }
4 class A implements iFille { }
5
6 $a = new A;
7
8 if ($a instanceof iParent)
9 {
10 echo 'Si iParent est une classe , alors $a est une instance de
iParent ou $a instancie une classe qui est une fille de
iParent. Sinon , $a instancie une classe qui implmente
iParent ou une fille de iParent.';
11 }
12 else
13 {
14 echo 'Si iParent est une classe , alors $a n\'est pas une
instance de iParent et $a n\'instancie aucune classe qui
est une fille de iParent. Sinon , $a instancie une classe
qui n\'implmente ni iParent , ni une de ses filles.';
15 }
16 ?>
444
INSTANCEOF ET LES INTERFACES
Vous savez maintenant tous les comportements que peut adopter cet oprateur et tous
les eets quil peut produire (tout est crit dans le prcdent code).
En rsum
Loprateur instanceof permet de vrier la nature de la classe dont lobjet test
est une instance.
Cet oprateur permet de vrier quun certain objet est bien une instance dune
classe lle de telle classe.
Cet oprateur permet de vrier quun certain objet est une instance dune classe
implmentant telle interface, ou que lune de ses classes mre limplmente.
445
CHAPITRE 23. LOPRATEUR INSTANCEOF
446
Index
A
abstract . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102
abstration. . . . . . . . . . . . . . . . . . . . . . . . . . . . 101
accesseur. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
alias . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 214
annotation . . . . . . . . . . . . . . . . . . . . . . . . . . . 232
addendum. . . . . . . . . . . . . . . . . . . . . . . 233
application. . . . . . . . . . . . . . . . . . . . . . . . . . . 314
backend . . . . . . . . . . . . . . . . . . . . . . . . . 316
frontend. . . . . . . . . . . . . . . . . . . . . . . . . 316
ArrayIterator . . . . . . . . . . . . . . . . . . . . . . 185
as . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 214
attribut . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
attribut statique . . . . . . . . . . . . . . . . . . . . . . 36
auto-chargement . . . . . . . . . . . . . . . . . . . . . . 26
autoload . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
C
catch. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 191
class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
classe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
clone. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161
const. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
constructeur . . . . . . . . . . . . . . . . . . . . . . . . . . 24
D
DAO. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
E
encapsulation . . . . . . . . . . . . . . . . . . . . . . . . . . 6
ErrorException . . . . . . . . . . . . . . . . . . . . . 200
Exception . . . . . . . . . . . . . . . . . . . . . . . . . . . 190
exception . . . . . . . . . . . . . . . . . . . . . . . . . . . . 190
InvalidArgumentException . . . . 199
PDOException. . . . . . . . . . . . . . . . . . . 198
RuntimeException . . . . . . . . . . . . . . 198
extends . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95
F
final. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103
nalisation. . . . . . . . . . . . . . . . . . . . . . . . . . . 103
G
getter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
H
hosts. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 317
hydratation . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
I
identiant dobjet . . . . . . . . . . . . . . . . . . . . 160
implements. . . . . . . . . . . . . . . . . . . . . . . . . . 170
injection de dpendances . . . . . . . . . . . . . 285
instance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
instanceof. . . . . . . . . . . . . . . . . . . . . . . . . . 440
interaction UML . . . . . . . . . . . . . . . . . . . . . 246
agrgation. . . . . . . . . . . . . . . . . . . . . . . 248
association . . . . . . . . . . . . . . . . . . . . . . 248
composition . . . . . . . . . . . . . . . . . . . . . 249
hritage . . . . . . . . . . . . . . . . . . . . . . . . . 246
interface. . . . . . . . . . . . . . . . . . . . . . . . . 247
interface . . . . . . . . . . . . . . . . . . . . . . . . . . . 170
interface prdnie . . . . . . . . . . . . . . . . . . . 174
ArrayAccess . . . . . . . . . . . . . . . . . . . . . 178
Countable . . . . . . . . . . . . . . . . . . . . . . . 181
Itarator . . . . . . . . . . . . . . . . . . . . . . . . . 174
SeekableIterator . . . . . . . . . . . . . . . . . 176
SplObserver . . . . . . . . . . . . . . . . . . . . . 274
SplSubject . . . . . . . . . . . . . . . . . . . . . . 274
447
INDEX
L
late static bindings . . . . . . . . . . . . . . . . . . . 105
linarisation. . . . . . . . . . . . . . . . . . . . . . . . . .147
serialize . . . . . . . . . . . . . . . . . . . . . . 148
unserialize . . . . . . . . . . . . . . . . . . . . 148
M
manager . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
masque de conception. . . . . . . . . . . . . . . . 271
mthode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
methode magique
__clone. . . . . . . . . . . . . . . . . . . . . . . . . 161
mthode magique . . . . . . . . . . . . . . . . . . . . 137
__call . . . . . . . . . . . . . . . . . . . . . . . . . . 146
__callStatic . . . . . . . . . . . . . . . . . . . . 146
__construct . . . . . . . . . . . . . . . . . . . . . 24
__destruct . . . . . . . . . . . . . . . . . . . . . 138
__get. . . . . . . . . . . . . . . . . . . . . . . . . . . 140
__invoke . . . . . . . . . . . . . . . . . . . . . . . 154
__isset . . . . . . . . . . . . . . . . . . . . . . . . . 142
__set . . . . . . . . . . . . . . . . . . . . . . . . . . . 139
__set_state . . . . . . . . . . . . . . . . . . . . 153
__sleep. . . . . . . . . . . . . . . . . . . . . . . . . 149
__toString . . . . . . . . . . . . . . . . . . . . . 152
__unset . . . . . . . . . . . . . . . . . . . . . . . . 143
__wakeup . . . . . . . . . . . . . . . . . . . . . . 150
mthode statique. . . . . . . . . . . . . . . . . . . . . . 35
module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 316
mutateur. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
MVC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 318
N
new . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
O
objet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
P
Paamayim Nekudotayim . . . . . . . . . . . . . . 31
parent. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
procdural . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
proprit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
R
reexivite
ReectionClass . . . . . . . . . . . . . . . . . . 218
ReectionMethod. . . . . . . . . . . . . . . . 227
ReectionObject . . . . . . . . . . . . . . . . 218
ReectionParameter . . . . . . . . . . . . . 239
ReectionProperty . . . . . . . . . . . . . . 223
rexivit . . . . . . . . . . . . . . . . . . . . . . . . . . . . 218
rsolution statique la vole . . . . . . . . . 105
S
self . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
set_error_handler . . . . . . . . . . . . . . . . . 200
set_exception_handler. . . . . . . . . . . . . 202
setter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
spl_autoload_register. . . . . . . . . . . . . . 27
static. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
satic:: . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106
T
this . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
throw. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 190
trait . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 207
trigger_error . . . . . . . . . . . . . . . . . . . . . . 202
try . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 191
U
UML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 242
use . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 207
V
visibilit. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .7
private . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
protected . . . . . . . . . . . . . . . . . . . . . . 100
public . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
448
Dpt lgal : dcembre 2012
ISBN : 979-10-90085-36-7
Code diteur : 979-10-90085
Imprim en France
Achev dimprimer le 3 dcembre 2012
sur les presses de Corlet Imprimeur (Cond-sur-Noireau)
Numro imprimeur : 151588
Mentions lgales :
Conception couverture : Fan Jiyong
Illustrations chapitres : Fan Jiyong