J'ai mes trois fichiers : class Personnage, class PersonnagesManager, index.
J'ai ajouté des personnages, cela fonctionne. Même s'il me reste à comprendre pourquoi je peux les ajouter plusieurs fois si je rafraichie la page...
Par contre, si j'essaie d'effacer un personnage, je reçois une jolie erreur "Warning: PDO::exec(): SQLSTATE[42000]: Syntax error or access violation: 1064 Erreur de syntaxe près de '' à la ligne 1 in C:\wamp64\www\revisions_oops\envoi\PersonnagesManager.php on line 40"
Pourtant, je ne vois pas d'erreur dans ma fonction delete... Je ne comrpends pas ce qui cloche.
Est-ce que quelqu'un pourrait me dire où est mon erreur car je n'en vois aucune... donc je ne comprends pas du tout comment la régler.
<?php
class PersonnagesManager
{
//Création de l'attribut db pour la connection à la database
private $_db;
// Le constructeur appelle setDb à la création d'un objet
public function __construct($db)
{
$this->setDb($db);
}
public function setDb(PDO $db)
{
$this->_db = $db;
}
// getter d'id
public function id()
{
return $this->_id;
}
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()
{
// Retourne la liste de tous les personnages.
$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();
}
}
?>
Personnages :
<?php
class Personnage
{
private $_id;
private $_nom;
private $_forcePerso;
private $_degats;
private $_niveau;
private $_experience;
public function __construct(array $donnees)
{
$this->hydrate($donnees);
}
public function hydrate(array $donnees)
{
// on doit récupérer tous les setters sans préciser lesquels et combien il y en a donc on fait une boucle foreach()
foreach ($donnees as $key => $value)
{
// On récupère le nom du setter correspondant à l'attribut.
// on récupère dans une variable $method (car on récupère une methode setSomething())
// les setters sont setForce par exemple, ici $key correspond à force et ucfirst vise à mettre la première lettre suivant le set en majuscule comme setForce
$method = 'set'.ucfirst($key);
// Si le setter correspondant existe.
// method_exists(nom de l'objet, nom de la methode)
if (method_exists($this, $method))
{
// On appelle le setter.
// l'objet appelle alors la methode avec pour paramètres les valeurs
$this->$method($value);
}
}
}
// GETTERS
public function id()
{
//attention ici : ne pas oublier l'underscore
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;
}
// SETTERS
public function setId($id)
{
$id= (int) $id;
if($id > 0)
{
$this->_id=$id;
}
}
public function setNom($nom)
{
if(is_string($nom))
{
$this->_nom=$nom;
}
}
public function setForcePerso($forcePerso)
{
// Attention : pour vérifier que l'id est un nombre entier, il faut d'abord demander à ce que ce soit un nombre puis mettre une condition pour dire que le nombre doit être supérieur à 0
$forcePerso= (int) $forcePerso;
if($forcePerso >= 0 && $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;
}
}
}
?>
Ta méthode PersonnagesManager::add ne met pas à jour l'attribut _id avec la valeur générée par l'auto-incrément (cf PDO::lastInsertId) or PersonnagesManager::delete repose dessus.
Et comme cet attribut _id vaut/reste à NULL, ça donne '' (chaîne vide) après cast en string et donne cette erreur SQL (il manque l'entier qu'attend le SGBD)
=> il faut juste ajouter $perso->setId($this->_db->lastInsertId()); après l'execute dans PersonnagesManager::add
L'idéal pour moi c'est d'utiliser au maximum les fonctions prepare et bindValue de PDO pour détecter plus facilement ce genre d'erreur.
// fonction actuelle
public function delete(Personnage $perso)
{
$this->_db->exec('DELETE FROM personnages WHERE id = '.$perso->id());
}
/**
* DELETE : ma version avec prepare et bindValue
*
* @param integer $id
* @return boolean
*/
public function delete(Personnage $perso): bool
{
$sql = "DELETE FROM personnages WHERE id = :id";
$query = $this->_db->prepare($sql);
$query->bindValue(':id', $perso->getId(), PDO::PARAM_INT);
return $query->execute();
}
Ce code ne permet pas de résoudre le problème (id à null) mais au moins ça marquera comme erreur "PDO can not convert null to int" ou quelque chose de proche qui permet de mieux déterminer l'origine du problème et le corriger plus facilement.
Je sais que c'est très tentant d'utiliser le raccourcis exec mais vérifier ce qu'on fait (surtout sur une requête de type DELETE) ça reste préférable.
Je dirais plutôt que ça "générerait" WHERE id = NULL, ce qui ne matcherait aucune ligne et le DELETE donnerait l'impression d'avoir "fonctionné" (pas d'erreur mais aucune MàJ)
J'ai presque envie de dire que PDO::exec est mieux. Fonctionnellement, PDO::exec l'est. Niveau sécurité, pas idéal par rapport à du préparé mais sur un AI, à moins de faire n'importe quoi, pas de raison de se manger une faille. A la rigueur avec de la cast et/ou du typehinting sur le getter, c'est blindé.
× Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
× Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
julp.fr ~ PHP < 8.0.0 : activer les erreurs PDO/SQL ~ PHP < 8.1.0 : activer les erreurs mysqli
julp.fr ~ PHP < 8.0.0 : activer les erreurs PDO/SQL ~ PHP < 8.1.0 : activer les erreurs mysqli