tite fractale

Modif 1ecbeb89

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.

 ![Schéma MVC sans M ni V](img/mvc/mvc_c.png)

 [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`.
+
+![Schéma MVC sans V](img/mvc/mvc_mc.png)
+
+[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`.
-
-![Schéma MVC sans V](img/mvc/mvc_mc.png)
-
-[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";
+}









Page générée le 20/12/2013, 22h30'42" (page virtuelle).
historique global