Date: Fri Dec 20 22:26:48 2013 +0100
Grosses modifs MVC PHP suite à la séance.
diff --git a/input/activite_intro_MVC_PHP.md b/input/activite_intro_MVC_PHP.md
index f421275..bc3c3ea 100644
--- a/input/activite_intro_MVC_PHP.md
+++ b/input/activite_intro_MVC_PHP.md
@@ -12,7 +12,7 @@ les structurer en séparant trois concepts :
* le `MODÃLE`, *modél*-isant la couche métier et en assurant la persistance,
* la `VUE`, qui sert dâinterface avec lâutilisateur, le plus souvent en lecture,
-* le `CONTROLLEUR`, qui orchestre le tout et contient le plus gros de la
+* le `CONTRÃLEUR`, qui orchestre le tout et contient le plus gros de la
logique de lâapplication.
Une telle séparation permet de :
@@ -47,7 +47,7 @@ Au cours de cette activité, nous allons construire une application web de type
* *Create*, créer, insérer,
* *Read*, lire,
-* *Update*, mettre à jour, changer,
+* *Update*, mettre à jour, changer, modifier,
* *Delete*, supprimer.
Voir [lâarticle Wikipedia correspondant](https://fr.wikipedia.org/wiki/CRUD).
@@ -74,6 +74,9 @@ Wikipedia](https://fr.wikipedia.org/wiki/Test_Driven_Development).
Pour cela nous utiliserons [urlrulz](https://github.com/Grahack/urlrulz).
+Tant quâon est dans les bonnes pratiques, un suivi des versions de votre
+travail peut être intéressant.
+
Contrôleur
==========
@@ -93,30 +96,45 @@ Certains traitements consisteront en une lecture ou une écriture dans la
couche de persistance, au travers donc du `M`. Pour afficher les résultats,
il les transmettra à `V` qui se chargera de la présentation.
-URL mapping
------------
+Pour simplifier
+---------------
-### Pour simplifier
+* Dans un premier temps, nous allons comprendre son rôle sans le `M` ni le `V`.
+* Télécharger (ou cloner) [my.very.cote](https://github.com/Grahack/my.very.cute)
+ à un endroit accessible par le serveur, pourquoi pas dans un sous-répertoire
+ `mvc` de la racine.
+* Dans `index.php`, bien configurer la constante `BASE`, lâadresse à laquelle
+ le répertoire contenant `index.php` sera accessible, par exemple :
+ `http://127.0.0.1/mvc/`.
+* Parfois, le contrôleur est constitué de plusieurs classes, réparties dans
+ différents fichiers, afin de mieux en organiser lâimplémentation. Ici, nous
+ utiliserons deux fichiers :
+ * `index.php`, le répartiteur (ou *dispatcher* en anglais),
+ * `controller.php`, le contrôleur.

[crédits schéma](http://book.cakephp.org/2.0/en/cakephp-overview/understanding-model-view-controller.html)
-* Dans un premier temps, nous allons comprendre son rôle sans le `M` ni le `V`.
-* Télécharger et désarchiver [C.zip](dl/C.zip) à un endroit accessible par le
- serveur.
-* Parfois, le contrôleur est constitué de plusieurs classes, réparties dans
- différents fichiers, afin de mieux en organiser lâimplémentation. Ici, nous
- nâutiliserons quâun seul contrôleur, réparti dans un deux fichier :
- * `index.php`,
- * `controller.php`.
+{- spoiler("Pourquoi deux fichiers ?",
+"""La distinction répartiteur-contrôleur est une coquetterie. Câest juste
+bien dans un *framework* dâavoir un fichier fixe (même sâil contient en peu de
+config, qui mériterait dâêtre externalisée), et un fichier dans lequel on
+code.""") }}}
-Pourquoi deux fichiers ?
+Activité
+--------
Après avoir lu lâintégralité de ces deux fichiers, tester à la main quelques
-URL et les réponses.
+URL et leur réponse. Essayez par exemple :
-### Plan du site, plan dâaction
+* dâafficher la page dâaccueil, Ã la racine du site,
+* de trouver lâURL qui ferait apparaître le texte `another method` qui est codé
+ dans le contrôleur,
+* dâajoutez des segments dans cette dernière URL et de voir comment la page
+ réagit.
+
+### URL mapping
Voici un exemple de plan classique dâun site de type CRUD :
@@ -138,6 +156,22 @@ URL | Description | C, R, U
<http://127.0.0.1/mvc/index.php/edit/3> + POST | modifier le perso n°3, confirmation | U
<http://127.0.0.1/mvc/index.php/delete/3> | supprimer le perso n°3, formulaire | D
+### Remarques
+
+* Après `index.php`, lâURL est composée de *segments* délimités par des `/`.
+* Il est possible dâutiliser la méthode `GET` pour encoder lâURL :
+ <http://127.0.0.1/mvc/index.php/perso/3/csv> pourrait devenir :
+ <http://127.0.0.1/mvc/index.php?page=perso&3&csv>.
+* Il est aussi possible dâavoir des [URL plus
+ propres](https://en.wikipedia.org/wiki/Clean_URL) en enlevant par exemple
+ `index.php` (besoin de configurer un *rewrite* sur le serveur) on en ajoutant
+ un faux `.html` (en fait en le supprimant dans `req_URL`).
+* Avec plusieurs contrôleurs, on pourrait avoir :
+ * <http://127.0.0.1/mvc/index.php/contrôleur/méthode/arg1/arg2>
+ * <http://127.0.0.1/mvc/controleur.php/méthode/arg1/arg2>
+
+### Codage du contrôleur
+
#### Existence seulement
Ãcrire les méthodes correspondantes à ce plan. Le site devra passer les tests
@@ -174,25 +208,13 @@ débrouillez-vous pour que le site passe les tests `urlrulz` ci-dessous
http://127.0.0.1/mvc/index.php/perso/3 perso 3 html
http://127.0.0.1/mvc/index.php/perso/3/html perso 3 html
http://127.0.0.1/mvc/index.php/perso/3/csv perso 3Â csv
- http://127.0.0.1/mvc/index.php/perso/5 perso 3 html
- http://127.0.0.1/mvc/index.php/perso/5/html perso 3 html
- http://127.0.0.1/mvc/index.php/perso/5/csv perso 3Â csv
+ http://127.0.0.1/mvc/index.php/perso/5 perso 5 html
+ http://127.0.0.1/mvc/index.php/perso/5/html perso 5 html
+ http://127.0.0.1/mvc/index.php/perso/5/csv perso 5Â csv
http://127.0.0.1/mvc/index.php/nouveau nouveau
http://127.0.0.1/mvc/index.php/edit/3 modif perso 3
http://127.0.0.1/mvc/index.php/delete/3 suppression perso 3
-Remarques
----------
-
-* Il est possible dâutiliser la méthode `GET` pour encoder lâURL :
- <http://127.0.0.1/mvc/index.php/perso/3/csv> pourrait devenir :
- <http://127.0.0.1/mvc/index.php?page=perso&3&csv>.
-* Avec plusieurs contrôleurs, on pourrait avoir :
- * <http://127.0.0.1/mvc/index.php/contrôleur/méthode/arg1/arg2>
- * <http://127.0.0.1/mvc/controleur.php/méthode/arg1/arg2>
-* Il est possible dâavoir des URL plus propres (enlever `index.php`, ajouter
- un faux `.html`â¦).
-
Modèle
======
@@ -209,6 +231,15 @@ dâautres termes, il réalise une abstraction sous deux angles :
reste de lâapplication ne sera pas impacté par un changement du support de
stockage (CSV, XML, MySQL, PGSQLâ¦).
+Pour simplifier
+---------------
+
+Nous nâincorporons pas pour lâinstant le `V`.
+
+
+
+[crédits](http://book.cakephp.org/2.0/en/cakephp-overview/understanding-model-view-controller.html)
+
Tests
-----
@@ -218,19 +249,29 @@ Il est possible dâutiliser des tests unitaires contre le modèle. Voir
Activité
--------
-### Pour simplifier
-
-Nous nâincorporons pas pour lâinstant le `V`.
-
-
-
-[crédits](http://book.cakephp.org/2.0/en/cakephp-overview/understanding-model-view-controller.html)
-
### Mise en place
-* Télécharger et désarchiver [MC.zip](dl/MC.zip) à un endroit accessible par le
- serveur.
-* Notez les deux lignes ajoutées dans `controller.php` pour inclure les modèles.
+* Télécharger les fichiers correspondants au modèle, à déposer dans le même
+ répertoire que `index.php` :
+ * [m_Personnage.class.php](squelette_m_Personnage.class.php.html),
+ * [m_PersonnagesManager.class.php](squelette_m_PersonnagesManager.class.php.html).
+* Télécharger [la correction du travail
+ précédent](squelette_controller_methodes_sans_db.php.html) et comparez-là Ã
+ votre version.
+* Dans cette correction, une préparation au travail suivant est proposée :
+ * les deux lignes ajoutées en tête de fichier pour inclure les modèles et
+ autres gestionnaires,
+ * un exemple de accès au modèle.
+
+ // Include model files.
+ foreach (glob("m_*.php") as $filename) include $filename;
+
+ public function la_suite() {
+ $mng = new PersonnagesManager();
+ foreach ($mng->getList() as $perso) {
+ echo $perso->getNom();
+ }
+ }
### Lectures sans HTML
@@ -330,9 +371,26 @@ Activité
On utilise la procédure `view` avec deux arguments seulement. Le HTML est
directement envoyé au navigateur.
-* Télécharger et désarchiver [C.zip](dl/MVC.zip) à un endroit accessible par le
- serveur.
-* Déplacer tout le code du contrôleur vers des fichiers vue.
+* Télécharger les vues, à déposer dans le même répertoire que `index.php` :
+ * [v_liste_persos.php](squelette_v_liste_persos.php.html),
+ * [v_liste_persos_csv.php](squelette_v_liste_persos_csv.php.html).
+* Déplacer tout le code du contrôleur vers des fichiers vue en suivant cet
+ exemple (Ã affiner) :
+
+ :
+
+ public function persos($display_type = 'html') {
+ $mng = new PersonnagesManager();
+ if ($display_type == 'html') {
+ header("Content-Type:text/html");
+ view('v_liste_persos.php', array('persos' => $mng->getList()));
+ } else if ($display_type == 'csv') {
+ header("Content-Type:text/plain");
+ echo view('v_liste_persos_csv.php', array('persos' => $mng->getList()), true);
+ } else {
+ echo "Type inconnu.";
+ }
+ }
Notez que si votre version de PHP le permet, il est possible dâutiliser `<?=` Ã
la place de `<? echo`. Câest plus joli dans les vues.
diff --git a/input/squelettes/controller_methodes_sans_db.php b/input/squelettes/controller_methodes_sans_db.php
new file mode 100644
index 0000000..2400819
--- /dev/null
+++ b/input/squelettes/controller_methodes_sans_db.php
@@ -0,0 +1,38 @@
+<?php
+dbg("Controller loaded.");
+
+// Include model files.
+foreach (glob("m_*.php") as $filename) include $filename;
+
+// OO controller.
+class Controller {
+ public function index() {
+ echo "accueil";
+ }
+ public function persos($type="html")
+ {
+ echo "liste ".$type;
+ }
+ public function perso($id, $type="html")
+ {
+ echo "perso ".$id." ".$type;
+ }
+ public function nouveau()
+ {
+ echo "nouveau";
+ }
+ public function edit($id)
+ {
+ echo "modif perso ".$id;
+ }
+ public function delete($id)
+ {
+ echo "suppression perso ".$id;
+ }
+ public function la_suite() {
+ $mng = new PersonnagesManager();
+ foreach ($mng->getList() as $perso) {
+ echo $perso->getNom();
+ }
+ }
+}
diff --git a/input/squelettes/controller_methodes_vides.php b/input/squelettes/controller_methodes_vides.php
new file mode 100644
index 0000000..3f8ec3b
--- /dev/null
+++ b/input/squelettes/controller_methodes_vides.php
@@ -0,0 +1,18 @@
+<?php
+//dbg("Controller loaded.");
+
+// OO controller.
+class Controller {
+ public function index() {
+ }
+ public function persos($type="html") {
+ }
+ public function perso($id, $type="html") {
+ }
+ public function nouveau() {
+ }
+ public function edit($id) {
+ }
+ public function delete($id) {
+ }
+}
diff --git a/input/squelettes/m_Personnage.class.php b/input/squelettes/m_Personnage.class.php
new file mode 100644
index 0000000..49da63f
--- /dev/null
+++ b/input/squelettes/m_Personnage.class.php
@@ -0,0 +1,95 @@
+<?php
+/* ---------------------------------------------------------------------------------------
+ - Déclaration :
+ des propriétés privées,
+ des constantes,
+ et des méthodes de la classe Personnage.
+ ------------------------------------------------------------------------------------- */
+class Personnage
+
+{ /* Propriétés privées de la classe Personnage
+ --------------------------------------------- */
+ private $id; // identifiant du personnage
+ private $nom; // nom du personnage
+ private $puissance; // puissance du personnage
+ private $degats; // dégâts du personnage
+ private $niveau; // niveau du personnage
+ private $experience; // expérience du personnage
+
+ /* Déclaration de constantes en rapport avec la puissance */
+ const puissancePetite = 20;
+ const puissanceMoyenne = 50;
+ const puissanceGrande = 80;
+
+ // permettra de modifier la valeur du texte sans entrer dans la fonciton qui l'affiche
+ private static $texte = "Je vais te frapper ";
+
+ /* Constructeur de la classe Personnage - le nom du personnage est passé au constructeur
+ le constructeur de la classe reçoit en paramètre d'entrée $Donnees
+ $donnees est l'équivalent d'un enregsitrement de la table personnages
+ en PHP le constructeur s'appelle toujours __construct
+ ---------------------------------------------------------------------------------------- */
+ public function __construct(array $Donnees)
+ {
+ $this->Hydrater($Donnees);
+ }
+
+ /* Accesseurs - get - des propriétés privées de la classe
+ --------------------------------------------------------- */
+ public function getId() { return $this->id; }
+ public function getNom() { return $this->nom; }
+ public function getpuissance() { return $this->puissance; }
+ public function getDegats() { return $this->degats; }
+ public function getNiveau() { return $this->niveau; }
+ public function getExperience() { return $this->experience; }
+
+ /* Mutateurs - set - des propriétés privées
+ ------------------------------------------- */
+ public function setId($Id) { $this->id = $Id; }
+ public function setNom($Nom) { $this->nom = $Nom; }
+ public function setDegats($Degats) { $this->degats = $Degats; }
+ public function setNiveau($Niveau) { $this->niveau = $Niveau; }
+ public function setPuissance($Puissance)
+ {
+ // On vérifie que la puissance du personnage est égale aux valeurs constantes pré définies //
+ if (in_array($Puissance, array(self::puissancePetite, self::puissanceMoyenne, self::puissanceGrande)))
+ {
+ $this->puissance = $Puissance;
+ } else {
+ // ??
+ }
+ }
+ public function setExperience($Experience)
+ {
+ // On vérifie que l'expérience du personnage est <= à 80 //
+ if (!is_numeric($Experience))
+ {
+ trigger_error("L'expérience du personnage doit être un entier", E_USER_WARNING);
+ return;
+ }
+ if ($Experience > 80)
+ {
+ trigger_error("L'expérience du personnage est limitée à 80" , E_USER_WARNING);
+ return;
+ }
+ $this->experience = $Experience;
+ }
+
+ // Hydratation de l'objet Personnage avec les données d'un enregistrement de la table personnages
+ public function Hydrater(array $donnees)
+ {
+ foreach ($donnees as $key => $value)
+ {
+ /* On récupère le nom du setter ou clé correspondant à l'attribut en cours de traitement
+ .ucfirst permet de convertir en majuscule le premier caractère du setter exemple setNom */
+ $method = 'set'.ucfirst($key);
+ if (method_exists($this, $method)) // Si le setter correspondant à la propriété privée existe
+ {
+ $this->$method($value); // On appelle le setter et on lui affecte la valeur
+ } else {
+ // ??
+ }
+ }
+ }
+}
+?>
\ No newline at end of file
diff --git a/input/squelettes/m_PersonnagesManager.class.php b/input/squelettes/m_PersonnagesManager.class.php
new file mode 100644
index 0000000..fcf7c83
--- /dev/null
+++ b/input/squelettes/m_PersonnagesManager.class.php
@@ -0,0 +1,57 @@
+<?php
+class PersonnagesManager
+{
+ private $bdd; // Instance de PDO
+ public function __construct()
+ {
+ $this->bdd = new PDO('mysql:host=localhost;dbname=personnages', 'root', '');
+ }
+
+ // Ajout
+ public function add(Personnage $perso)
+ {
+ $q = $this->bdd->prepare('INSERT INTO personnages SET nom = :nom, puissance = :puissance, degats = :degats, niveau = :niveau, experience = :experience');
+ $q->bindValue(':nom', $perso->getNom());
+ $q->bindValue(':puissance', $perso->getPuissance(), PDO::PARAM_INT);
+ $q->bindValue(':degats', $perso->getDegats(), PDO::PARAM_INT);
+ $q->bindValue(':niveau', $perso->getNiveau(), PDO::PARAM_INT);
+ $q->bindValue(':experience', $perso->getExperience(), PDO::PARAM_INT);
+ $q->execute();
+ }
+ // Suppression
+ public function delete(Personnage $perso)
+ {
+ $this->bdd->exec('DELETE FROM personnages WHERE id = '.$perso->id());
+ }
+ // Recherche
+ public function get($id)
+ {
+ $id = (int) $id;
+ $q = $this->bdd->query('SELECT id, nom, puissance, degats, niveau, experience FROM personnages WHERE id = '.$id);
+ $donnees = $q->fetch(PDO::FETCH_ASSOC);
+ return new Personnage($donnees);
+ }
+ // Récupération de l'ensemble des enregistrements
+ public function getList()
+ {
+ $persos = array();
+ $q = $this->bdd->query('SELECT id, nom, puissance, degats, niveau, experience FROM personnages ORDER BY nom');
+ while ($donnees = $q->fetch(PDO::FETCH_ASSOC))
+ {
+ $persos[] = new Personnage($donnees);
+ }
+ return $persos;
+ }
+ // Mise à jour
+ public function update(Personnage $perso)
+ {
+ $q = $this->bdd->prepare('UPDATE personnages SET puissancePerso = :puissance, degats = :degats, niveau = :niveau, experience = :experience WHERE id = :id');
+ $q->bindValue(':puissancePerso', $perso->puissance(), PDO::PARAM_INT);
+ $q->bindValue(':degats', $perso->degats(), PDO::PARAM_INT);
+ $q->bindValue(':niveau', $perso->niveau(), PDO::PARAM_INT);
+ $q->bindValue(':experience', $perso->experience(), PDO::PARAM_INT);
+ $q->bindValue(':id', $perso->id(), PDO::PARAM_INT);
+ $q->execute();
+ }
+}
+?>
\ No newline at end of file
diff --git a/input/squelettes/urlz_c2.txt b/input/squelettes/urlz_c2.txt
index 1142b3f..08bdacd 100644
--- a/input/squelettes/urlz_c2.txt
+++ b/input/squelettes/urlz_c2.txt
@@ -7,9 +7,9 @@ http://127.0.0.1/mvc/index.php/persos/csv liste csv
http://127.0.0.1/mvc/index.php/perso/3 perso 3 html
http://127.0.0.1/mvc/index.php/perso/3/html perso 3 html
http://127.0.0.1/mvc/index.php/perso/3/csv perso 3Â csv
-http://127.0.0.1/mvc/index.php/perso/5 perso 3 html
-http://127.0.0.1/mvc/index.php/perso/5/html perso 3 html
-http://127.0.0.1/mvc/index.php/perso/5/csv perso 3Â csv
+http://127.0.0.1/mvc/index.php/perso/5 perso 5 html
+http://127.0.0.1/mvc/index.php/perso/5/html perso 5 html
+http://127.0.0.1/mvc/index.php/perso/5/csv perso 5Â csv
http://127.0.0.1/mvc/index.php/nouveau nouveau
http://127.0.0.1/mvc/index.php/edit/3 modif perso 3
http://127.0.0.1/mvc/index.php/delete/3 suppression perso 3
diff --git a/input/squelettes/v_liste_persos.php b/input/squelettes/v_liste_persos.php
new file mode 100644
index 0000000..53fb5f6
--- /dev/null
+++ b/input/squelettes/v_liste_persos.php
@@ -0,0 +1,5 @@
+<ul>
+<?php foreach ($vars['persos'] as $perso) { ?>
+ <li><?= $perso->getNom(); ?></li>
+<?php } ?>
+</ul>
diff --git a/input/squelettes/v_liste_persos_csv.php b/input/squelettes/v_liste_persos_csv.php
new file mode 100644
index 0000000..349a382
--- /dev/null
+++ b/input/squelettes/v_liste_persos_csv.php
@@ -0,0 +1,5 @@
+<?php
+foreach ($vars['persos'] as $perso) {
+ echo implode(',', array($perso->getId(), $perso->getNom()));
+ echo "\n";
+}