Mis à jour le lundi 8 janvier 2018
  • 30 heures
  • Moyenne

Ce cours est visible gratuitement en ligne.

Ce cours existe en livre papier.

Vous pouvez obtenir un certificat de réussite à l'issue de ce cours.

Vous pouvez être accompagné et mentoré par un professeur particulier par visioconférence sur ce cours.

J'ai tout compris !

Manipulation de données stockées

Connectez-vous ou inscrivez-vous gratuitement pour bénéficier de toutes les fonctionnalités de ce cours !

Tout site web dynamique doit être capable de sauvegarder des données afin de les utiliser ultérieurement. Comme vous le savez sans doute, l'un des moyens les plus simples et efficaces est la base de données permettant de sauvegarder des centaines de données et de les récupérer en un temps très court.

Cependant, la gestion des données stockées en BDD peut être assez difficile à cerner en POO. Le débutant ne sait en général pas par où commencer et se pose tout un tas de questions : où créer la connexion avec la BDD ? Où placer les requêtes ? Comment traiter les valeurs retournées ? Nous allons justement voir tout cela ensemble. Accrochez-vous bien, un TP suivra pour voir si vous avez bien tout compris !

Une entité, un objet

Rappels sur la structure d'une BDD

Commençons ce chapitre par observer la structure d'une table en BDD et, plus précisément, la manière dont sont stockées les données. Comme vous le savez sûrement, une table n'est autre qu'un grand tableau où sont organisées les données :

Exemple d'une table de personnages
Exemple d'une table de personnages

Nous voyons ici que notre table contient 3 personnages. Ces données sont stockées sous forme d'entrées. C'est sous cette forme que le gestionnaire de la BDD (MySQL, PostgreSQL, etc.) manipule les données. Si l'on regarde du côté de l'application qui les manipule (ici, notre script PHP), on se rend compte que c'est sous une forme similaire que les données sont récupérées. Cette forme utilisée, vous la connaissez bien : vous utilisiez jusqu'à présent des tableaux pour les manipuler. Exemple :

<?php
// On admet que $db est un objet PDO
$request = $db->query('SELECT id, nom, forcePerso, degats, niveau, experience FROM personnages');
    
while ($perso = $request->fetch(PDO::FETCH_ASSOC)) // Chaque entrée sera récupérée et placée dans un array.
{
  echo $perso['nom'], ' a ', $perso['forcePerso'], ' de force, ', $perso['degats'], ' de dégâts, ', $perso['experience'], ' d\'expérience et est au niveau ', $perso['niveau'];
}

Ça, si vous avez déjà fait un site internet de A à Z, vous avez du l'écrire pas mal de fois !

Maintenant, puisque nous programmons de manière orientée objet, nous voulons travailler seulement avec des objets (c'est le principe même de la POO, rappelons-le) et non plus avec des tableaux. En d'autres termes, il va falloir transformer le tableau que l'on reçoit en objet.

Travailler avec des objets

Avant de travailler avec des objets, encore faut-il savoir de quelle classe ils sont issus. Dans notre cas, nous voulons des objets représentant des personnages. On aura donc besoin d'une classePersonnage!

<?php
class Personnage
{

}

Vient maintenant une question embêtante dans votre tête pour construire cette classe : « Je commence par où ? »

Une classe est composée de deux parties (éventuellement trois) :

  • une partie déclarant les attributs. Ce sont les caractéristiques de l'objet ;

  • une partie déclarant les méthodes. Ce sont les fonctionnalités de chaque objet ;

  • éventuellement, une partie déclarant les constantes de classe. Nous nous en occuperons en temps voulu.

Lorsque l'on veut construire une classe, il va donc falloir systématiquement se poser les mêmes questions :

  • Quelles seront les caractéristiques de mes objets ?

  • Quelles seront les fonctionnalités de mes objets ?

Les réponses à ces questions aboutiront à la réalisation du plan de la classe, le plus difficile. ;)

Commençons donc à réfléchir sur le plan de notre classe en répondant à la première question : « Quelles seront les caractéristiques de mes objets ? » Pour vous mettre sur la piste, regardez de nouveau le tableau de la BDD contenant les entrées, cela vous donnera peut-être la réponse… Et oui, les attributs de notre classe (les caractéristiques) nous sont offerts sur un plateau ! Ils sont listés en haut du tableau :id,nom,forcePerso,degats,niveauetexperience.

Écrivons maintenant notre classe :

<?php
class Personnage
{
  private $_id;
  private $_nom;
  private $_forcePerso;
  private $_degats;
  private $_niveau;
  private $_experience;
}

Il faut bien sûr implémenter (écrire) les getters et setters qui nous permettront d'accéder et de modifier les valeurs de notre objet. Pour rappel, un getter est une méthode chargée de renvoyer la valeur d'un attribut, tandis qu'un setter est une méthode chargée d'assigner une valeur à un attribut en vérifiant son intégrité (si vous assignez la valeur sans aucun contrôle, vous perdez tout l'intérêt qu'apporte le principe d'encapsulation).

Pour construire nos setters, il faut donc nous pencher sur les valeurs possibles de chaque attribut :

  • les valeurs possibles de l'identifiant sont tous les nombres entiers strictement positifs ;

  • les valeurs possibles pour le nom du personnage sont toutes les chaînes de caractères ;

  • les valeurs possibles pour la force du personnage sont tous les nombres entiers allant de 1 à 100 ;

  • les valeurs possibles pour les dégâts du personnage sont tous les nombres entiers allant de 0 à 100 ;

  • les valeurs possibles pour le niveau du personnage sont tous les nombres entiers allant de 1 à 100 ;

  • les valeurs possibles pour l'expérience du personnage sont tous les nombres entiers allant de 1 à 100.

On en déduit donc le code de chaque setter. Voici donc ce que donnerait notre classe avec ses getters et setters :

<?php
class Personnage
{
  private $_id;
  private $_nom;
  private $_forcePerso;
  private $_degats;
  private $_niveau;
  private $_experience;
  
  // Liste des getters
  
  public function id()
  {
    return $this->_id;
  }
  
  public function nom()
  {
    return $this->_nom;
  }
  
  public function forcePerso()
  {
    return $this->_forcePerso;
  }
  
  public function degats()
  {
    return $this->_degats;
  }
  
  public function niveau()
  {
    return $this->_niveau;
  }
  
  public function experience()
  {
    return $this->_experience;
  }
  
  // Liste des setters
  
  public function setId($id)
  {
    // On convertit l'argument en nombre entier.
    // Si c'en était déjà un, rien ne changera.
    // Sinon, la conversion donnera le nombre 0 (à quelques exceptions près, mais rien d'important ici).
    $id = (int) $id;
    
    // On vérifie ensuite si ce nombre est bien strictement positif.
    if ($id > 0)
    {
      // Si c'est le cas, c'est tout bon, on assigne la valeur à l'attribut correspondant.
      $this->_id = $id;
    }
  }
  
  public function setNom($nom)
  {
    // On vérifie qu'il s'agit bien d'une chaîne de caractères.
    if (is_string($nom))
    {
      $this->_nom = $nom;
    }
  }
  
  public function setForcePerso($forcePerso)
  {
    $forcePerso = (int) $forcePerso;
    
    if ($forcePerso >= 1 && $forcePerso <= 100)
    {
      $this->_forcePerso = $forcePerso;
    }
  }
  
  public function setDegats($degats)
  {
    $degats = (int) $degats;
    
    if ($degats >= 0 && $degats <= 100)
    {
      $this->_degats = $degats;
    }
  }
  
  public function setNiveau($niveau)
  {
    $niveau = (int) $niveau;
    
    if ($niveau >= 1 && $niveau <= 100)
    {
      $this->_niveau = $niveau;
    }
  }
  
  public function setExperience($experience)
  {
    $experience = (int) $experience;
    
    if ($experience >= 1 && $experience <= 100)
    {
      $this->_experience = $experience;
    }
  }
}
?>

Reprenons notre code de tout à l'heure, celui qui nous permettait de lire la BDD. Modifions-le un peu pour qu'on puisse manipuler des objets et non des tableaux :

<?php
// On admet que $db est un objet PDO.
$request = $db->query('SELECT id, nom, forcePerso, degats, niveau, experience FROM personnages');
    
while ($donnees = $request->fetch(PDO::FETCH_ASSOC)) // Chaque entrée sera récupérée et placée dans un array.
{
  // On passe les données (stockées dans un tableau) concernant le personnage au constructeur de la classe.
  // On admet que le constructeur de la classe appelle chaque setter pour assigner les valeurs qu'on lui a données aux attributs correspondants.
  $perso = new Personnage($donnees);
        
  echo $perso->nom(), ' a ', $perso->forcePerso(), ' de force, ', $perso->degats(), ' de dégâts, ', $perso->experience(), ' d\'expérience et est au niveau ', $perso->niveau();
}

Et quelles seront les méthodes de nos classes ?

Les méthodes concernent des fonctionnalités que possède l'objet, des actions qu'il peut effectuer. Voyez-vous des fonctionnalités intéressantes à implémenter ? Pour les opérations basiques que l'on effectue, il n'y en a pas besoin. En effet, nous voulons juste créer des objets et assigner des valeurs aux attributs, donc hormis les getters et setters, aucune autre méthode n'est nécessaire !

D'accord, nous avons des objets maintenant. Mais à quoi cela sert-il, concrètement ?

Il est sûr que dans cet exemple précis, cela ne sert à rien. Mais vous verrez plus tard qu'il est beaucoup plus intuitif de travailler avec des objets et par conséquent beaucoup plus pratique, notamment sur de grosses applications où de nombreux objets circulent un peu dans tous les sens.

L'hydratation

La théorie de l'hydratation

L'hydratation est un point essentiel dans le domaine de la POO, notamment lorsqu'on utilise des objets représentant des données stockées. Cette notion peut vite devenir compliquée et créer des interrogations pour un développeur débutant si elle est abordée de manière approximative, alors que des explications claires prouvent qu'il n'y a rien de compliqué là-dedans.

Quand on vous parle d'hydratation, c'est qu'on parle d'« objet à hydrater ». Hydrater un objet, c'est tout simplement lui apporter ce dont il a besoin pour fonctionner. En d'autres termes plus précis, hydrater un objet revient à lui fournir des données correspondant à ses attributs pour qu'il assigne les valeurs souhaitées à ces derniers. L'objet aura ainsi des attributs valides et sera en lui-même valide. On dit que l'objet a ainsi été hydraté.

Schématiquement, une hydratation se produit comme ceci :

Hydratation d'un objet
Hydratation d'un objet

Au début, nous avons un objetPersonnagedont les attributs sont vides. Comme le schéma le représente, l'hydratation consiste à assigner des valeurs aux attributs. Ainsi l'objet est fonctionnel car il contient des attributs valides : nous avons donc bien hydraté l'objet.

L'hydratation en pratique

Comme on l'a vu, hydrater un objet revient à assigner des valeurs à ses attributs. Qu'avons-nous besoin de faire pour réaliser une telle chose ? Il faut ajouter à l'objet l'action de s'hydrater. Et qui dit action dit méthode !

Nous allons donc écrire notre fonctionhydrate():

<?php
class Personnage
{
  private $_id;
  private $_nom;
  private $_forcePerso;
  private $_degats;
  private $_niveau;
  private $_experience;

  // Un tableau de données doit être passé à la fonction (d'où le préfixe « array »).
  public function hydrate(array $donnees)
  {
 
  }

  public function id() { return $this->_id; }
  public function nom() { return $this->_nom; }
  public function forcePerso() { return $this->_forcePerso; }
  public function degats() { return $this->_degats; }
  public function niveau() { return $this->_niveau; }
  public function experience() { return $this->_experience; }

  public function setId($id)
  {
    // L'identifiant du personnage sera, quoi qu'il arrive, un nombre entier.
    $this->_id = (int) $id;
  }
        
  public function setNom($nom)
  {
    // On vérifie qu'il s'agit bien d'une chaîne de caractères.
    // Dont la longueur est inférieure à 30 caractères.
    if (is_string($nom) && strlen($nom) <= 30)
    {
      $this->_nom = $nom;
    }
  }

  public function setForcePerso($forcePerso)
  {
    $forcePerso = (int) $forcePerso;
            
    // On vérifie que la force passée est comprise entre 0 et 100.
    if ($forcePerso >= 0 && $forcePerso <= 100)
    {
      $this->_forcePerso = $forcePerso;
    }
  }

  public function setDegats($degats)
  {
    $degats = (int) $degats;

    // On vérifie que les dégâts passés sont compris entre 0 et 100.
    if ($degats >= 0 && $degats <= 100)
    {
      $this->_degats = $degats;
    }
  }

  public function setNiveau($niveau)
  {
    $niveau = (int) $niveau;

    // On vérifie que le niveau n'est pas négatif.
    if ($niveau >= 0)
    {
      $this->_niveau = $niveau;
    }
  }

  public function setExperience($exp)
  {
    $exp = (int) $exp;

    // On vérifie que l'expérience est comprise entre 0 et 100.
    if ($exp >= 0 && $exp <= 100)
    {
      $this->_experience = $exp;
    }
  }
}

Vient maintenant l'aspect le plus important : que doit-on mettre dans cette méthode ? Souvenez-vous de sa fonction : elle doit hydrater l'objet, c'est-à-dire « assigner les valeurs passées en paramètres aux attributs correspondant ». Cependant, je vous le dis avant que vous fassiez fausse route : il ne faut pas assigner ces valeurs directement, comme ceci :

<?php
// …
public function hydrate(array $donnees)
{
  if (isset($donnees['id']))
  {
    $this->_id = $donnees['id'];
  }
    
  if (isset($donnees['nom']))
  {
    $this->_nom = $donnees['nom'];
  }
    
  // …
}
// …

La principale raison pour laquelle c'est une mauvaise façon de procéder, est qu'en agissant ainsi vous violez le principe d'encapsulation. En effet, de cette manière, vous ne contrôlez pas l'intégrité des valeurs. Qui vous garantit que le tableau contiendra un nom valide ? Rien du tout! Comment contrôler alors l'intégrité de ces valeurs ? Regardez un peu votre classe, la réponse est sous vos yeux… C'est le rôle des setters de faire ces vérifications ! Il faudra donc les appeler au lieu d'assigner les valeurs directement :

<?php
// …
public function hydrate(array $donnees)
{
  if (isset($donnees['id']))
  {
    $this->setId($donnees['id']);
  }

  if (isset($donnees['nom']))
  {
    $this->setNom($donnees['nom']);
  }

  // …
}
// …

Théoriquement, nous avons terminé le travail. Cependant, cela n'est pas très flexible : si vous ajoutez un attribut (et donc son setter correspondant), il faudra modifier votre méthodehydrate()pour ajouter la possibilité d'assigner cette valeur. De plus, avec les 6 attributs actuellement créés, il est long de vérifier si la clé existe puis d'appeler la méthode correspondante.

Nous allons donc procéder plus rapidement : nous allons créer une boucle qui va parcourir le tableau passé en paramètre. Si le setter correspondant à la clé existe, alors on appelle ce setter en lui passant la valeur en paramètre. En langage naturel, l'algorithme peut s'écrire de cette façon :

PARCOURS du tableau $donnees (avec pour clé $cle et pour valeur $valeur)
  On assigne à $setter la valeur « 'set'.$cle », en mettant la 
  première lettre de $cle en majuscule (utilisation de ucfirst())
  SI la méthode $setter de notre classe existe ALORS
    On invoque $setter($valeur)
  FIN SI
FIN PARCOURS

Pas de panique, je vais vous expliquer chaque ligne. Dans votre tête, prenez un exemple de valeur pour$donnees:

<?php
$donnees = [
  'id' => 16,
  'nom' => 'Vyk12',
  'forcePerso' => 5,
  'degats' => 55,
  'niveau' => 4,
  'experience' => 20
];

Vous voyez bien que chaque clé correspond à un attribut de notre objet, à qui on assigne une valeur précise. Dans notre méthodehydrate(), lorsqu'on parcourt notre tableau, on a successivement la cléid, puisnom, puisforcePerso, etc., avec leur valeur correspondante.

En récupérant le nom de l'attribut, il est facile de déterminer le setter correspondant. En effet, chaque setter a pour nomsetNomDeLAttribut.

Pour l'instant notre méthode ressemble à ceci :

<?php
// …
public function hydrate(array $donnees)
{
  foreach ($donnees as $key => $value)
  {
    $method = 'set'.ucfirst($key);
    // …
  }
}
// …

Il faut maintenant vérifier que la méthode existe. Pour cela, nous allons utiliser la fonction method_exists(). Elle prend en premier paramètre le nom de la classe ou une instance de cette classe, et en deuxième paramètre le nom de la méthode qui nous intéresse. La méthode renvoietruesi la méthode existe, sinonfalse.

Je vous laisse ajouter cette condition qui permet de voir si le setter correspondant existe.

Voici la correction :

<?php
// …
public function hydrate(array $donnees)
{
  foreach ($donnees as $key => $value)
  {
    $method = 'set'.ucfirst($key);
        
    if (method_exists($this, $method))
    {
      // …
    }
  }
}
// …

Et maintenant, il ne reste plus qu'à appeler le setter à l'intérieur de la condition ! Pour cela, je vais vous apprendre une petite astuce. Il est possible d'appeler une méthode dynamiquement, c'est-à-dire appeler une méthode dont le nom n'est pas connu à l'avance (en d'autres termes, le nom est connu seulement pendant l'exécution et est donc stocké dans une variable). Pour ce faire, rien de plus simple ! Regardez ce code :

<?php
class A
{
  public function hello()
  {
    echo 'Hello world !';
  }
}

$a = new A;
$method = 'hello';

$a->$method(); // Affiche : « Hello world ! »

Vous êtes maintenant capables de créer la dernière instruction dans notre méthode ! Pour rappel, celle-ci doit invoquer le setter dont le nom est contenu dans la variable$method.

<?php
// …
public function hydrate(array $donnees)
{
  foreach ($donnees as $key => $value)
  {
    // On récupère le nom du setter correspondant à l'attribut.
    $method = 'set'.ucfirst($key);
        
    // Si le setter correspondant existe.
    if (method_exists($this, $method))
    {
      // On appelle le setter.
      $this->$method($value);
    }
  }
}
// …

Cette fonction est très importante, vous la retrouverez dans de nombreux codes (parfois sous des formes différentes) provenant de plusieurs développeurs. Gardez-là donc dans un coin de votre tête.

Gérer sa BDD correctement

On vient de voir jusqu'à présent comment gérer les données que les requêtes nous renvoient, mais où placer ces requêtes ? Notre but est de programmer orienté objet, donc nous voulons le moins de code possible en-dehors des classes pour mieux l'organiser. Beaucoup de débutants sont tentés de placer les requêtes dans des méthodes de la classe représentant une entité de la BDD. Par exemple, dans le cas du personnage, je parie que la plupart d'entre vous seraient tentés d'écrire les méthodesadd()etupdate()dans la classePersonnage, ayant respectivement pour rôle d'exécuter une requête pour ajouter et modifier un personnage en BDD.

Arrêtez-vous là tout de suite ! Je vais vous expliquer pourquoi vous faites fausse route, puis vous montrerai la bonne marche à suivre.

Une classe, un rôle

En POO, il y a une phrase très importante qu'il faut que vous ayez constamment en tête : « Une classe, un rôle. » Maintenant, répondez clairement à cette question : « Quel est le rôle d'une classe commePersonnage» ?

Un objet instanciant une classe commePersonnagea pour rôle de représenter une ligne présente en BDD. Le verbe « représenter » est ici très important. En effet, « représenter » est très différent de « gérer ». Une ligne de la BDD ne peut pas s'auto-gérer ! C'est comme si vous demandiez à un ouvrier ayant construit un produit de le commercialiser : l'ouvrier est tout à fait capable de le construire, c'est son rôle, mais il ne s'occupe pas du tout de sa gestion, il en est incapable. Il faut donc qu'une deuxième personne intervienne, un commercial, qui va s'occuper de vendre ce produit.

Pour revenir à nos objets d'origine, nous aurons donc besoin de quelque chose qui va s'occuper de les gérer. Ce quelque chose, vous l'aurez peut-être deviné, n'est autre qu'un objet. Un objet gérant des entités issues d'une BDD est généralement 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 créant un gestionnaire pour nos personnages, qui va donc se charger d'en ajouter, d'en modifier, d'en supprimer et d'en récupérer. Puisque notre classe est un gestionnaire de personnages, je vous propose de la nommerPersonnagesManager. Cependant, rappelez-vous les questions que l'on doit se poser pour établir le plan de notre classe :

  • Quelles seront les caractéristiques de mes objets ?

  • Quelles seront les fonctionnalités de mes objets ?

Les caractéristiques d'un manager

Partie délicate, car cela est moins intuitif que de trouver les caractéristiques d'un personnage. Pour trouver la réponse, vous devrez passer par une autre question (ce serait trop simple de toujours répondre à la même question !) : « De quoi a besoin un manager pour fonctionner ? »

Même si la réponse ne vous vient pas spontanément à l'esprit, vous la connaissez : elle a besoin d'une connexion à la BDD pour pouvoir exécuter des requêtes. En utilisant PDO, vous devriez savoir que la connexion à la BDD est représentée par un objet, un objet d'accès à la BDD (ou DAO pour Database Access Object). Vous l'avez 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 d'autre chose pour fonctionner ?

Non, c'est tout. Vous pouvez donc commencer à écrire votre classe :

<?php
class PersonnagesManager
{
  private $_db;
}

Les fonctionnalités d'un manager

Pour pouvoir gérer au mieux des entités présentes en BDD (ici, nos personnages), il va falloir quelques fonctionnalités de base. Quelles sont-elles ?

Un manager doit pouvoir :

  • enregistrer une nouvelle entité ;

  • modifier une entité ;

  • supprimer une entité ;

  • sélectionner une entité.

C'est le fameux CRUD (Create, Read, Update, Delete). N'oublions pas aussi d'ajouter un setter pour notre manager afin de pouvoir modifier l'attribut$_db(pas besoin d'accesseur). La création d'un constructeur sera aussi indispensable si nous voulons assigner à cet attribut un objet PDO dès l'instanciation du manager.

Nous allons donc écrire notre premier manager en écrivant les méthodes correspondant à ces fonctionnalités. Écrivez dans un premier temps les méthodes en ne les remplissant que de commentaires décrivant les instructions qui y seront écrites. Cela vous permettra d'organiser clairement le code dans votre tête.

<?php
class PersonnagesManager
{
  private $_db; // Instance de PDO.

  public function __construct($db)
  {
    $this->setDb($db);
  }

  public function add(Personnage $perso)
  {
    // Préparation de la requête d'insertion.
    // Assignation des valeurs pour le nom, la force, les dégâts, l'expérience et le niveau du personnage.
    // Exécution de la requête.
  }

  public function delete(Personnage $perso)
  {
    // Exécute une requête de type DELETE.
  }

  public function get($id)
  {
    // Exécute une requête de type SELECT avec une clause WHERE, et retourne un objet Personnage.
  }

  public function getList()
  {
    // Retourne la liste de tous les personnages.
  }

  public function update(Personnage $perso)
  {
    // Prépare une requête de type UPDATE.
    // Assignation des valeurs à la requête.
    // Exécution de la requête.
  }

  public function setDb(PDO $db)
  {
    $this->_db = $db;
  }
}

Une fois cette étape accomplie, vous pouvez remplacer les commentaires par les instructions correspondantes. C'est la partie la plus facile car vous l'avez déjà fait de nombreuses fois en procédural : ce ne sont que des requêtes à taper et des valeurs à retourner.

<?php
class PersonnagesManager
{
  private $_db; // Instance de PDO

  public function __construct($db)
  {
    $this->setDb($db);
  }

  public function add(Personnage $perso)
  {
    $q = $this->_db->prepare('INSERT INTO personnages(nom, forcePerso, degats, niveau, experience) VALUES(:nom, :forcePerso, :degats, :niveau, :experience)');

    $q->bindValue(':nom', $perso->nom());
    $q->bindValue(':forcePerso', $perso->forcePerso(), 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->execute();
  }

  public function delete(Personnage $perso)
  {
    $this->_db->exec('DELETE FROM personnages WHERE id = '.$perso->id());
  }

  public function get($id)
  {
    $id = (int) $id;

    $q = $this->_db->query('SELECT id, nom, forcePerso, degats, niveau, experience FROM personnages WHERE id = '.$id);
    $donnees = $q->fetch(PDO::FETCH_ASSOC);

    return new Personnage($donnees);
  }

  public function getList()
  {
    $persos = [];

    $q = $this->_db->query('SELECT id, nom, forcePerso, degats, niveau, experience FROM personnages ORDER BY nom');

    while ($donnees = $q->fetch(PDO::FETCH_ASSOC))
    {
      $persos[] = new Personnage($donnees);
    }

    return $persos;
  }

  public function update(Personnage $perso)
  {
    $q = $this->_db->prepare('UPDATE personnages SET forcePerso = :forcePerso, degats = :degats, niveau = :niveau, experience = :experience WHERE id = :id');

    $q->bindValue(':forcePerso', $perso->forcePerso(), 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();
  }

  public function setDb(PDO $db)
  {
    $this->_db = $db;
  }
}

Essayons tout ça !

Faisons un petit test pour s'assurer que tout cela fonctionne.

Commençons par créer un objetPersonnage:

<?php
$perso = new Personnage([
  'nom' => 'Victor',
  'forcePerso' => 5,
  'degats' => 0,
  'niveau' => 1,
  'experience' => 0
]);

Maintenant, comment l'ajouter en BDD ? Il faudra d'abord créer une instance de notre manager, en lui passant une instance de PDO.

<?php
$perso = new Personnage([
  'nom' => 'Victor',
  'forcePerso' => 5,
  'degats' => 0,
  'niveau' => 1,
  'experience' => 0
]);
    
$db = new PDO('mysql:host=localhost;dbname=tests', 'root', '');
$manager = new PersonnagesManager($db);

Nous n'avons plus qu'une instruction à entrer : celle ordonnant l'ajout du personnage en BDD. Et c'est tout !

<?php
$perso = new Personnage([
  'nom' => 'Victor',
  'forcePerso' => 5,
  'degats' => 0,
  'niveau' => 1,
  'experience' => 0
]);

$db = new PDO('mysql:host=localhost;dbname=tests', 'root', '');
$manager = new PersonnagesManager($db);
    
$manager->add($perso);

Il est maintenant temps de mettre en pratique ce que vous venez d'apprendre. Cependant, avant de continuer, assurez-vous bien de tout avoir compris, sinon vous serez perdus car vous ne comprendrez pas le code du TP !

En résumé

  • Du côté du script PHP, chaque enregistrement de la base de données est représenté par un objet possédant une liste d'attributs identique à la liste des colonnes de la table.

  • Il doit être possible d'hydrater chacun des objets grâce à une méthodehydratequi a pour rôle d'assigner les valeurs passées en paramètre aux attributs correspondant.

  • La communication avec la BDD se fait par le biais d'un objet différent de l'objet représentant l'enregistrement (une classe = un rôle). Un tel objet est un manager.

  • Un manager peut stocker les objets en BDD, mais peut tout-à-fait les stocker sur un autre support (fichier XML, fichier texte, etc.).

Exemple de certificat de réussite
Exemple de certificat de réussite