singleton et une class pour lister
Bonjour.
Un singleton et une class categorie.
J'ai un peu de difficulté avec ma requête préparé pour lister.
<?php
//db connection class using singleton pattern
class dbConn
{
//variable to hold connection object.
protected static $db;
//private construct - class cannot be instatiated externally.
private function __construct()
{
try
{
// assign PDO object to db variable
self::$db = new PDO('mysql:host=localhost;dbname=philipp4_bdd', 'root', '');
self::$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
catch(PDOException $e)
{
//Output error - would normally log this to error file rather than output to user.
echo "Connection Error: " . $e->getMessage();
}
}
// get connection function. Static method - accessible without instantiation
public static function getConnection()
{
//Guarantees single instance, if no connection object exists then create one.
if(!self::$db)
{
//new connection object.
new dbConn();
}
//return connection.
return self::$db;
}
} //end class
$db = dbConn::getConnection();
class Categorie
{
private $id;
private $cat;
public function __construct($id = 0, $cat = "")
{
$this->id = $id;
$this->cat = $cat;
$this->errormsg = "";
}
public function getId()
{
return $this->id;
}
public function getCat()
{
return $this->cat;
}
public function setId($id)
{
$this->id = $id;
return $id;
}
public function setCat($cat)
{
$this->cat = $cat;
return $cat;
}
public function message()
{
echo $this->errormsg;
}
public static function llist()
{
$sql = "SELECT * FROM categorie WHERE id = :id";
$sql = dbConn::getConnection()->prepare($sql);
$sql->bindParam(":id", self::$id, PDO::PARAM_INT);
$sql->execute();
$res = fetchAll(PDO::FETCH_OBJ);
return $res;
}
}
$category = new Categorie(55);
$category->message();
foreach(Categorie::llist() as $k):
echo $k->id . ' ' . $k->cat;
endforeach;
Ce qui me préoccupe, c'est situé à cette ligne:
public static function llist()
Je ne sais plus comment faire ici.
De l'aide me serait grandement utile.
Réponses apportées à cette discussion
Tu ne sais pas comment faire quoi au juste ?
Sur ta class dbConn :
- Pourquoi as-tu mis la propriété $db en protected au lieu de private ? Ça aurait été logique si ta classe Categorie étendait dbConn, mais là ce n'est pas le cas. As-tu saisi la différence entre protected et private ?
Quelques remarques sur ta classe Categorie :
- le return dans les méthodes setCat et setId n'a pas de sens : dans une méthode set, on définit la valeur d'une propriété de l'objet, on n'en demande pas sa valeur, le retour n'a donc pas lieu d'être.
- Pourquoi ta méthode llist() est-elle statique ? COnsidère qu'une méthode statique peut être appelée sans création d'instance de la clase, ça veut donc dire que tu pourrais appeler cette méthode sans avoir initialisé la valeur de l'identifiant : demande-toi alors à quoi ressemblerait la requête SQL qui serait envoyée à ta base de données.
- Toujours dans ta méthode llist(), tu définis d'abord une variable $sql en lui affectant ue requête SQL, puis la lignes suivante, tu définis à nouveau la même variable en lui affectant un objet PDO : je suggère de renommer cette dernière;
Par ailleurs, tu as eu la bonne idée de créer un singleton pour établir ta connexion, mais tu ne t'en sers pas dans ta classe Categorie : tu pourrais le faire de la manière suivante : ajouter une propriété $db à ta classe Categorie et lors de l'instanciation de cette dernière, lui passer ta variable $db en paramètre au même titre que tu lui passes $id et $cat.
Enfin L j'ai encore édité ton message parce que tu n'utilises pas la bonne manière. Il faut utiliser la syntaxe Markdown. Essaye d'en tenir compte, ça facilitera la lecture des sutres qui ne peuvent pas intervenir.
Bonjour et merci pour la réponse.
J'ai tenté de suivre un tutoriel pour les requêtes SELECT dans une Classe. Pour le Markdown, j'avais bien mi les balises mais j'avais oublié de mettre celles de fermeture; quand j'ai voulu corriger, j'ai pas été capable de le faire (je ne voyais pas le bouton Editer).
J'ai tenté plusieurs variantes de plusieurs tutoriels à un autre. Je suis allé aussi dans le site de PHP pour mieux cerner au sujet des Classes PHP. Et les tutoriels vidéos, tu dois souvent te taper une heure pour quelque minutes d'information en plus de prendre beaucoup de bande passante.
Mais, je vais suivre la logique de vos explications en tentant de corriger mon code. Ça devrait peut-être allé si je fait comme il faut les choses. Je tente les corrections et je vais y revenir.
Bonjour :-)
J'ai apporté des corrections, pour montrer:
<?php
class dbConn
{
private static $db;
private function __construct()
{
try
{
self::$db = new PDO('mysql:host=localhost;dbname=bdd', 'root', 'pass');
self::$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
catch(PDOException $e)
{
echo "Connection Error: " . $e->getMessage();
}
}
public static function getConnection()
{
if(!self::$db)
{
new dbConn();
}
return self::$db;
}
}
$db = dbConn::getConnection();
class Categorie
{
private $id;
private $cat;
private $db;
public function __construct($id = 0, $cat = "", $db=null)
{
$this->id = $id;
$this->cat = $cat;
$this->errormsg = "";
}
public function getId()
{
return $this->id;
}
public function getCat()
{
return $this->cat;
}
public function setId($id)
{
$this->id = $id;
return $this->id;
}
public function setCat($cat)
{
$this->cat = $cat;
return $this->cat;
}
public function message()
{
echo $this->errormsg;
}
public function llist()
{
$s = "SELECT * FROM categorie WHERE id = :id";
$sql = dbConn::getConnection()->prepare($s);
$sql->bindParam(":id", $this->id, PDO::PARAM_INT);
$sql->execute();
$res = fetchAll(PDO::FETCH_OBJ);
return $res;
}
}
$category = new Categorie(55);
$category->message();
foreach(Categorie::llist() as $k):
echo $k->id . ' ' . $k->cat;
endforeach;
?>
Y a la partie requête qui me pose problème.
Fatal error: Using $this when not in object context on line 71
Private, la propriété ne sont pas accessible de l'extérieur de la class.
Protected, idem sauf pour la class enfant quand c'est extanded.
Pour private/protected, ok.
Pour les messages en Markdown, j'ai encore du éditer ton message : arrête d'utiliser <pre> ou <code>, ce n'est pas de la syntaxe Markdown. Pour le code, laisse d'abord une ligne vide, ensuite décale tout le bloc de code de quatre espace, comme si tu rajoutais une indentation sur l'ensemble du bloc de code. Et enfin laisse une ligne vide avant la suite de ton message.
Pour l'erreur ensuite :
Tu as retiré le « static »... mais tu appelles quand même la méthode llist() de façon statique : dbConn::llist() : en apparence, ça fonctionne, mais si tu configure ton php.ini en mode E_STRICT, tu vas voir un message d'erreur t'indiquant que tu ne dois pas appeler une méthode normale de façon statique.
La conséquence de ça, c'est que l'instance de ta classe Categorie n'existe pas, donc $this n'existe pas davantage dans ce contexte.
Il faudrait faire :
$catListe = $category->llist();
foreach($catListe as $k):
echo $k->id . ' ' . $k->cat;
endforeach;
Et là tu as de meilleures chances d'obtenir le résultat attendu.
Tiens, pour essayer pour le MarkDown..
Fatal error: Call to undefined function fetchAll()
J'ai bien laissé une ligne avant et après et décalé le tout de 4 espaces.
Pourtant, ils disent bien de placer les deux balises pour le block.
en même temps, j'indique mon message d'erreur rencontré.
Merci en passant.
PS, c'est bien ça pour le block. Je vais faire de même.
c'est dans la requête l'erreur.
ok, je viens de voir mon erreur:
$res = $sql->fetchAll(PDO::FETCH_OBJ);
Normal... Tu as du suivre les tutos sur PDO un peu en diagonale.
On va revoir un peu ton code. Commençons par ton singleton. L'intérêt d'un objet de ce type, c'est d'être portable. Ici, tu définis un certains nombre de paramètres en dur directement dans la classe : c'est une assez mauvaise pratique parce que si tu crée une autre application, tu devras éditer le code de cette classe pour modifier ces paramètres. Voici comment tu devrais envisager plutôt ton Singleton :
/**
* Singleton définissant une connexion PDO à une source de données.
*
* Utilisation :
* <code>
* $db = dbConn::getConnection($dsn, $user, $pswd, $options);
* </code>
*/
class dbConn
{
/**
* Instance de la classe.
* @var dbConn
*/
private static $db;
/**
* Construteur privé.
* C'est un Singleton, on ne peut donc pas instancier directement la classe.
* @see dbConn::getConnection()
*/
private function __construct($dsn, $user, $pswd, $options = null)
{
try
{
self::$db = new PDO($dsn, $user, $pswd, $options);
self::$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
catch(PDOException $e)
{
echo "Connection Error: " . $e->getMessage();
}
}
/**
* Appel du Singleton.
*
* Si l'instance n'existe pas, elle sera créée avant d'être retournée
* @param String $dsn Source des données
* @param String $user Utilisateur pouvant se connecter à la source de données
* @param String $pswd Mot de passe de l'utilisateur
* @param Array $options Options PDO (facultatif, voir doc PHP sur PDO)
*/
public static function getConnection($dsn, $user, $pswd, $options = null)
{
if(!self::$db)
{
new self($dsn, $user, $pswd, $options);
}
return self::$db;
}
}
J'ai modifié certains détails et ajouté des commentaires que je t'invite à lire attentivement.
Ensuite, j'ai modifié quelques détails dans ta classe Categorie pour refléter les changements dans le Singleton et la manière de l'utiliser. Je t'avais indiqué des éléments sur les paramètres à cette classe Categorie, mais tu ne les as que partiellement appliqués. Voici ta classe :
class Categorie
{
/**
* Colonne id de la table Categorie
* @var Integer
*/
private $id;
/**
* Colonne cat de la table Categorie
* @var String
*/
private $cat;
/**
* Instance de dbConn
* @var dbConn
*/
private $db;
/**
* Construteur.
* Définit les parametres de l'instance :
*
* @param dbConn $db
* @param Int $id
* @param String $cat
*/
public function __construct(dbConn $db, $id = 0, $cat = "")
{
$this->db = $db;
$this->id = $id;
$this->cat = $cat;
$this->errormsg = "";
}
public function getId()
{
return $this->id;
}
public function getCat()
{
return $this->cat;
}
public function setId($id)
{
$this->id = $id;
return $this->id;
}
public function setCat($cat)
{
$this->cat = $cat;
return $this->cat;
}
public function message()
{
echo $this->errormsg;
}
public function llist()
{
/* 1 : on construit la requête */
$sql = "SELECT * FROM categorie WHERE id = :id";
/* 2 : On prépare la requête qu'on encapsule dans un objet $odb */
$odb = $this->db->prepare($sql);
/* 3 : On définit la valeur des paramètrs de la requête préparée */
$odb->bindParam(":id", $this->id, PDO::PARAM_INT);
/* 4 : On exécute la requête */
$odb->execute();
/* 5 : On récupère le résultat */
$res = $odb->fetchAll(PDO::FETCH_OBJ);
return $res;
}
}
Regarde bien la méthode llist() et compare avec ta ropre version. Pose des questions si un élément t'échappe. Et tu pourras par la même occasion voir l'utilisation de fetchAll sans que ça génère une erreur : si tu ne comprends pas la différence, pose des questions.
Enfin, l'utilisation de tout ça :
d'abord, un fichier de configuration. J'ai dit plus haut que les classes devaient être portables, donc utilisables d'une application à l'autre avec un minimum de modification. Idéalement, on ne devrait jamais avoir à modifier quoi que ce soit d'une classe générique comme ton Singleton quelle que soit l'application. Donc on va définir un fichier de configuration avec ce code :
<?php
/**
* Paramètres permettant une connexion à une base de données avec PDO
*/
$dsn = 'mysql:host=localhost;dbname=bdd';
$user = 'root';
$pswd = 'pass';
$options = null;
?>
Maintenant, utilisons tout ceci :
include('chemin/vers/le/fichier/de/configuration.php');
/**
* On récupère une instance de la connexion
*/
$db = dbConn::getConnection($dsn, $user, $pswd, $options);
/**
* On récupère une isntance de la classe Categorie en lui passant
* une instance de la connexion et une valeur de l'identifiant de
* la ligne recherchée
*/
$category = new Categorie($db, 55);
$category->message();
/* On récupère les informations sur la ligne de données */
$catListe = $category->llist();
/* On affiche les paires clé/valeur de la ligne */
foreach($catListe as $k)
{
echo $k->id . ' ' . $k->cat;
}
Compare tout ça avec ton propre code et indique moi les éléments que tu ne comprends pas correctement s'il y a lieu.
Bon samedi.
Je vois des différences dans le code qui sont pour moi majeures. Je vais essayer de m'expliquer en mes mots.
J'ai un peu de misère à comprendre le déroulement entre la class singleton et la class categorie. Avant, la class categorie utilisait :: pour faire l'appel de la base de données directement dans la requete.
Il semble que là, c'est :
$db = dbConn::getConnection($dsn, $user, $pswd, $options);
$category = new Categorie($db, 55);
qui fait toute la différence.
Pour tester, j'ai assemblé: J'obtiens cette erreur:
Catchable fatal error: Argument 1 passed to Categorie::__construct() must be an instance of dbConn, integer given, called on line 88 and defined on line 42
Je vois que j'ai l'instanciation qui a une variable dedans. Lui fait appelle à
$db = dbConn::getConnection($dsn, $user, $pswd, $options);
Tien, ma correction complète:
<?php
class dbConn
{
private static $db;
private function __construct($dsn, $user, $pswd, $options = null)
{
try
{
self::$db = new PDO($dsn, $user, $pswd, $options);
self::$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
catch(PDOException $e)
{
echo "Connection Error: " . $e->getMessage();
}
}
public static function getConnection($dsn, $user, $pswd, $options = null)
{
if(!self::$db)
{
new self($dsn, $user, $pswd, $options);
}
return self::$db;
}
}
class Categorie
{
private $id;
private $cat;
private $db;
public function __construct(dbConn $db, $id = 0, $cat = "")
{
$this->db = $db;
$this->id = $id;
$this->cat = $cat;
$this->errormsg = "";
}
public function getId()
{
return $this->id;
}
public function getCat()
{
return $this->cat;
}
public function setId($id)
{
$this->id = $id;
return $this->id;
}
public function setCat($cat)
{
$this->cat = $cat;
return $this->cat;
}
public function message()
{
echo $this->errormsg;
}
public function llist()
{
$sql = "SELECT * FROM categorie WHERE id = :id";
$odb = $this->db->prepare($sql);
$odb->bindParam(":id", $this->id, PDO::PARAM_INT);
$odb->execute();
$res = $odb->fetchAll(PDO::FETCH_OBJ);
return $res;
}
}
$db = dbConn::getConnection($dsn, $user, $pswd, $options);
$category = new Categorie(99);
$category->message();
$catListe = $categorie->llist();
foreach($catListe as $k)
{
echo $k->id . ' ' . $k->cat;
}
?>
Le message d'erreur est pourtant explicite : s'il apparait, c'est que le paramètre qui a été passé n'est pas bon. Ce qui n'est pas bon, c'est donc la « nature » du paramètre : on attend une instance de la classe dbConf, or ce qui arrive est autre chose.
Juste après la ligne qui crée l'instance de dbConf, il pourrait être utile d'afficher ce qui est créé, avec par exemple quelque chose comme ceci :
$db = dbConn::getConnection($dsn, $user, $pswd, $options);
echo('<pre>');
var_dump($db);
echo('</pre>');
Ça va afficher ce que contient réellement la variable $db : teste ça et poste moi le retour ici, ça aidera à déboguer.
Ça indique ceci : object(PDO)[2]
Tu peux m'expliquer pour dbconn dans ce contexte?
public function __construct(dbConn $db,
C'est une manière de typer un argument. Ici, en l'occurrence, on indique le type du paramètre $db qui doit être un objet dbConf.
Autant pour moi : il y a une erreur dans le Singleton. Corrige comme ceci :
class dbConn
{
private static $db;
private function __construct($dsn, $user, $pswd, $options = null)
{
try
{
self::$db = new PDO($dsn, $user, $pswd, $options);
self::$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
catch(PDOException $e)
{
echo "Connection Error: " . $e->getMessage();
}
}
public static function getConnection($dsn, $user, $pswd, $options = null)
{
if(!self::$db)
{
self::$db = new self($dsn, $user, $pswd, $options);
}
return self::$db;
}
}
J'obtiens cet erreur suite à la correction :
object(dbConn)[1]
La ligne en cause, c'est laquelle?
Il faudrait que je vois la partie de code qui a généré ça pour avoir une idée.
À première vue, c'est ce qu'affiche le var_dump qui indique que cette fois, on a bien un objet dbConf. DOnc l'erreur fatale de tout à l'heure ne doit plus apparaitre.
Catchable fatal error: Argument 1 passed to Categorie::__construct() must be an instance of dbConn, integer given, called on line 87 and defined on line 42
Sans le code, je ne verrai pas où est l'erreur...
<?php
$dsn = 'mysql:host=localhost;dbname=bdd';
$user = 'root';
$pswd = '';
$options = null;
?>
<?php
class dbConn
{
private static $db;
private function __construct($dsn, $user, $pswd, $options = null)
{
try
{
self::$db = new PDO($dsn, $user, $pswd, $options);
self::$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
catch(PDOException $e)
{
echo "Connection Error: " . $e->getMessage();
}
}
public static function getConnection($dsn, $user, $pswd, $options = null)
{
if(!self::$db)
{
self::$db = new self($dsn, $user, $pswd, $options);
}
return self::$db;
}
}
class Categorie
{
private $id;
private $cat;
private $db;
public function __construct(dbConn $db, $id = 0, $cat = "")
{
$this->db = $db;
$this->id = $id;
$this->cat = $cat;
$this->errormsg = "";
}
public function getId()
{
return $this->id;
}
public function getCat()
{
return $this->cat;
}
public function setId($id)
{
$this->id = $id;
return $this->id;
}
public function setCat($cat)
{
$this->cat = $cat;
return $this->cat;
}
public function message()
{
echo $this->errormsg;
}
public function llist()
{
$sql = "SELECT * FROM categorie WHERE id = :id";
$odb = $this->db->prepare($sql);
$odb->bindParam(":id", $this->id, PDO::PARAM_INT);
$odb->execute();
$res = $odb->fetchAll(PDO::FETCH_OBJ);
return $res;
}
}
$db = dbConn::getConnection($dsn, $user, $pswd, $options);
var_dump($db);
$category = new Categorie(99);
$category->message();
$catListe = $categorie->llist();
foreach($catListe as $k)
{
echo $k->id . ' ' . $k->cat;
}
?>
Ok, je vois, l'erreur est ici :
$category = new Categorie(99);
Il manque un truc, observe bien.
Il manque un argument en rapport avec dbConn $db mais je ne vois pas quoi il manque. J'y ai passé la nuit. :(
Regarde la définition de la méthode elle-même. Elle est appelée avec un seul argument, or ça génère une erreur parce qu'il manque un argument, et celui envoyé est un entier et non une instance de dbConf.
Si tu relis attentivement les explications que j'ai indiquées au préalable, tu devrais logiquement trouver la réponse.
J'ai beau chercher pour l'erreur, il y a toujours une erreur.
$category = new Categorie($db, 99);
J'ajoute $db, chose que j'avais déjà fait, j'ai toujours une erreur.
Notice: Undefined variable: categorie on line 89
Fatal error: Call to a member function llist() on a non-object on line 89
Bon. si je mets comme ceci :
$catListe = $category->llist();
Fatal error: Call to undefined method dbConn::prepare() on line 74
ligne 74: $odb = $this->db->prepare($sql);
Si je mets comme ceci :
$catListe = $categorie->llist();
Notice: Undefined variable: categorie on line 89
Fatal error: Call to a member function llist() on a non-object on line 89
Dans nos exemple, c'est mis comme pour le 2e. Je ne sais plus là.
Désolé, c'est moi qui ai fait des erreurs dans les correctifs.
L'objet passé à ta classe Categorie ne doit pas être un objet dbConf mais un objet PDO. Donc la première erreur est dans la methode getConnection() de la classe dbConf, il faut faire simplement
new self($dsn, $user, $pswd, $options);
Au lieu de
self::$db = new self($dsn, $user, $pswd, $options);
Ensuite, dans le constructeur de ta classe Categorie, ce n'est pas un objet dbConf qui est attendu mais un objet PDO :
public function __construct(PDO $db, $id = 0, $cat = "")
{ // ... etc...
Essaye à nouveau en corrigeant ça et ça devrait fonctionner normalement.
Un détail aussi : $categorie !== $category : si tu initialise une variable, utilise la même par la suite, ça évitera des erreurs de variables indéfinies.
Tien, le tout fonctionne maintenant. Un grand merci pour ton temps !!!
C'est bien apprécié. Je vais réfléchir à tout ça pour la suite.
J'ai aussi mis :
$category = new Categorie($db, 88);
$category->message();
$catListe = $category->llist(); <<<<ici
remplacé categorie par category.
En principe, je devrais pouvoir ajouter les fonctions delete, update, insert en suivant ce principe.
Je test un insert:
public function insert()
{
$sql = " INSERT INTO categorie (cat) VALUES (:cat) ";
$odb = $this->db->prepare($sql);
$odb->bindParam(":cat", $this->cat, PDO::PARAM_STR, 60);
$odb->execute();
$this->id = $this->db->lastInsertId();
$odb->closeCursor();
$this->errormsg = ($odb->rowCount()===1) ? "Insertion réussie du ID $this->id<br>" : "Aucune insertion<br>";
}
Habituellement, je fais ceci :
//$category = new Categorie($db, 88);
//$category->message();
$category = new Categorie($db);
$category->insert("ajout"); <<<<<<ICI
$category->message();
Mais, là, il semblerait que c'est peut-être Categorie($db,"ajout")
Je réussi à àjouter un ID mais pas le CAT.
Au final, je cherche à faire un CRUD dans une class categorie.
J'ai trois bases comme ça.
Regarde ta définition de la méthode insert : elle n'attend aucun paramètre, il est donc normal que ce que tu envoie ne soit pas pris en compte.
Ceci étant, ça pourrait fonctionner à condition de procéder légèrement différemment. Dans ta classe Categorie, il faudrait utiliser les get/set pour initialiser les valeurs de tes colonnes. À partir de là, tu peux déclencher les opérations d'insertion/modification/suppression. Je te laisse y penser un moment, essaye d'apporter les ajustements à ton code pour faire ça et montre-moi les messages d'erreurs le cas échéant.
J'ajoute quand même un détail : il faudrait que ton système puisse déterminer automatiquement si la ligne à écrire est une insertion ou bien une modification, donc une requête INSERT ou une requête UPDATE.
J'ai changé ceci :
$odb->bindParam(":cat", $this->cat, PDO::PARAM_STR, 60);
$hits->cat est devenu $cat
insert($cat)
Je trouve curieux que $this->cat ne marche pas tandit que $cat oui.
Pas supposé vue que $cat est propriété private.
Dans ma nouvelle fonction, si je mets $id à la place de $this>id,
ça va afficher quand même ma liste alors que $id est private.
public function all_list()
{
$sql = "SELECT * FROM categorie";
$odb = $this->db->prepare($sql);
$odb->bindParam(":id", $id, PDO::PARAM_INT); <<<<<ICI
$odb->execute();
$res = $odb->fetchAll(PDO::FETCH_OBJ);
$odb->closeCursor();
return $res;
}
Je savais que je pouvais utiliser setCat("untruc") et enlever insert();
En tout cas, ça marche.
Te sers-tu de la fonction var_dump pour voir ce que contient réellement tel ou tel élément ?
Dans ta méthode, qu'est-ce qu'affiche var_dump($cat) et qu'est-ce qu'affiche var_dump($this->cat) ?
Dans insert():
var_dump($cat); <<<<affiche mon insert
var_dump($this->cat); <<<<<affiche rien du tout
Dans all_list():
var_dump($id); <<<<<affiche null
var_dump($this->id); <<<<<affiche le no id
si je supprime insert() pour remplacer par setCat(), ça ne marche pas comme j'ai dit.
Mais pourquoi ça insert quand même et que ça affiche aussi avec :
$id et $cat
Bon alors, à ICI, avec $cat, c'est ok mais pas avec $this->cat :
public function insert($cat)
{
$sql = " INSERT INTO categorie (cat) VALUES (:cat) ";
$odb = $this->db->prepare($sql);
$odb->bindParam(":cat", $cat, PDO::PARAM_STR, 60); <<<<ICI
$odb->execute();
$this->id = $this->db->lastInsertId();
$odb->closeCursor();
}
et ICI, si je mets $id ou $this->id, ça va marcher.
public function all_list()
{
$sql = "SELECT * FROM categorie";
$odb = $this->db->prepare($sql);
$odb->bindParam(":id", $id, PDO::PARAM_INT); <<<<<<ICI
$odb->execute();
$res = $odb->fetchAll(PDO::FETCH_OBJ);
$odb->closeCursor();
return $res;
}
Tu fais des confusions : pense en terme d'objet. Ton objet Categorie a deux propriétés, id et cat. Les méthodes get/set servent à récupérer ou définir la valeur de ces propriétés. Ensuite, tu as les méthodes CRUD pour manipuler les données de la base.
N'oublie pas non plus un point essentiel : une instance ne représente qu'une seule ligne de ta table.
Donc, essaye de te représenter la chose suivante :
- L'instance avec deux propriétés;
- les méthodes set qui te permettent d'initialiser la valeur des propriétés, et tu n'es pas obligé d'initialiser les deux, tu pourrais enregistrer une nouvelle catégorie, donc définir id à null et cat avec une valeur;
- une méthode privée insert() qui va créer une requête INSERT;
- une méthode privée update() qui va créer une requête UPDATE;
- une méthode publique sauvegarder() qui va appeler l'une des deux méthodes précédentes selon que $this->id a une valeur ou non.
Essaye de partir là-dessus en tâchant de réfléchir à ce que tu veux faire/obtenir.
Bonjour Cyrano et vous tous et toutes.
J'essaie de penser "Objet" chose que j'ai beaucoup moins de difficulté depuis que je me suis inscrit y a peu de temps. Mais, je n'arrive pas à résoudre le pourquoi du $cat au lieu de $this->cat dans ce bout de code.
public function insert($cat)
{
$sql = " INSERT INTO categorie (cat) VALUES (:cat) ";
$odb = $this->db->prepare($sql);
$odb->bindParam(":cat", $this->cat, PDO::PARAM_STR, 60);
$odb->execute();
$this->id = $this->db->lastInsertId();
$odb->closeCursor();
Et surtout pourquoi je peux quand même placer une variable à la place.
Je suis dans la réflexion sur l’ensemble du code pour essayer de comprendre ligne par ligne. Je tente la chose.
Dans le cas présent, ce n'est pas compliqué : ta méthode insert() comporte un paramètre : donc dans ta méthode, tu as le choix entre la propriété de l'objet $this->cat ou le paramètre de la méthode $cat, les deux pouvant avoir des valeurs qui n'ont rien de commun.
Donc si on veut rester logique, ta méthode insert() ne devrait pas avoir de paramètre du tout. Par contre, tu devrais relire ma précédente réponse expliquant trois méthodes pour les écritures en base.
Il y a un autre aspect que je n'ai pas abordé qui éclairerait peut-être un tout petit peu : dans une base de données, il faudrait toujours considérer une clé primaire comme une donnée système qu'il faut éviter de manipuler soi-même, on laisse le SGBD le gérer. En d'autres termes, on ne devrait jamais écrire soi-même la valeur d'une clé primaire. Par contre, on a besoin parfois de connaitre la valeur d'une clé primaire pour accéder à telle ou telle ligne dans la table. C'est sur cette base que j'ai suggéré trois méthodes pour ajouter ou modifier des données, trois dont deux privées. Partant de là, l'opération de base consiste à initialiser l'objet à partir d'une clé primaire existant ou non, et ensuite on ne peut appeler que la méthode sauvegarder qui va déterminer s'il faut faire une insertion ou une mise à jour selon que la valeur de la clé primaire existe ou non.
Je ne sais pas si ça éclaire mieux ta lanterne, je sais que la POO et l'accès aux données n'a rien de forcément très évident, même à expliquer...
Bonjour Cyrano.
Je vais faire un bout de lecture du manuel PHP:
http://www.manuelphp.com/php/language.oop5.paamayim-nekudotayim.php
Tout ce qui touche les Classes afin d'améliorer mes connaissances,
et je vais y revenir. Donc, un petit délais me sera nécessaire.
:-) Merci pour ton aide.
C'est en utilisant des exemples concrets que j'ai appris le procédural (PDO) sur le site de PHP mais des exemples en Orienté Objet, y en pas plus qu'il faut. Alors, je laisse tombé l'objet, je n'arrive pas à réaliser quelque chose de fonctionnel comme un simple CRUD avec un singleton de connexion. Merci toute de même pour l'aide que vous m'avez apportez, je continue au procédural, beaucoup plus simple et au moins j'avance. Là, je perds trop de temps dans mon projet. Je dois avancer dans mes affaires. J'ai tenté le coup mais ça donne rien de bon pour moi.
Salut,
c'est pas dramatique, ça viendra avec le temps et l'expérience.
Au début, on a toujours du mal à saisir l'intérêt réel de la POO par rapport au procédural, jusqu'au jour où on arrive à créer un premier objet utile, fonctionnel et généralement très basique. Plus tard, on intègre d'autres notions complémentaires et les objets gagnent en complexité de conception, mais il faut garder à l'esprit l'idée de la simplicité d'utilisation.
Commencer avec du CRUD en POO n'est en outre pas le chemin le plus facile, il y a beaucoup de choses à considérer et ça devient rapidement un casse-tête infernal.
Mais garde toujours en mémoire le principe DRY (Don't Repeat Yourself), ce qui signifie, si un code est répété à plusieurs endroits d'une même application, il y a certainement un moyen de factoriser ça, et on en arrive à créer des petits objets qui font la même chose : on a plus qu'à nettoyer le code des redondances en remplaçant par des appels à l'objet. Le jour où on fait un correctif ou une amélioration à la classe, c'est toute l'application qui est mise à jour en une seule fois.
Bon courage :)
Salut Cyrano. Tes mots d'encouragement m'aide à persévérer.
Bon. J'ai évaluer d'une autre manière mon code et voici:
<?php
require_once 'pdo_mysql.php';
abstract class Model extends pdo_mysql {
private $conn;
private $id;
public function __construct() {
$this->conn = pdo_mysql::pdo_connection();
}
public function select($id) {
$stmt = $this->conn->prepare("SELECT * FROM $this->table WHERE id=:id");
$stmt->bindValue(':id', $id, PDO::PARAM_INT);
$stmt->execute();
$rows = $stmt->fetchAll(PDO::FETCH_OBJ);
return $rows;
}
public function getAll() {
$stmt = $this->conn->prepare("SELECT * FROM $this->table");
$stmt->execute();
return $stmt->fetchAll(PDO::FETCH_OBJ);
}
}
class Categorie extends Model {
protected $table='categorie';
}
$test = new Categorie();
$c = $test->getAll();
foreach ($c as $a) {
echo $a->cat .'<br>';
}
?>
et le singleton :
<?php
define('USERNAME2','root');
define('PASSWORD2','');
define('DSN2',"mysql:host=localhost;dbname=bdd");
class pdo_mysql {
private static $_instance;
public static function &pdo_connection() {
if (!self::$_instance) {
try {
self::$_instance = new PDO(DSN2, USERNAME2, PASSWORD2);
self::$_instance->setAttribute(PDO::ATTR_PERSISTENT,true);
self::$_instance->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION);
} catch (PDOException $est) {
die("pdo connection error! ". $est->getMessage() ."<br/>");
}
}
return self::$_instance;
}
private function __construct() {
}
private function __clone() {
}
}
?>
Pas mal. Je vais quand même te proposer une suggestion pour le premier fichier, les modèles.
Là, tu bloques la manière de nommer tes colonnes de clés primaires. Si tu tombes sur une base de données déjà existante avec une manière de nommer les colonnes autrement, tu vas avoir un problème. Donc il faudrait que la propriété puisse être définie dans chaque classe étendue. Voilà la proposition, il y aura quelques commentaires ensuite ;
<?php
require_once 'pdo_mysql.php';
abstract class Model extends pdo_mysql
{
private $_conn;
protected $_pk;
/**
* Constructeur
* Définit la connexion à la base de données
*/
public function __construct()
{
$this->_conn = pdo_mysql::pdo_connection();
}
/**
* Sélection d'une ligne de données pour un identifiant.
* @param Int $id
* @return Object
*/
public function select($id)
{
$stmt = $this->_conn->prepare("SELECT * FROM ". $this->table ." WHERE ". $this->_pk ."=:id");
$stmt->bindValue(':id', $id, PDO::PARAM_INT);
$stmt->execute();
$rows = $stmt->fetchAll(PDO::FETCH_OBJ);
return $rows;
}
/**
* Sélection le contenu de la table.
* @return Object
*/
public function getAll()
{
$stmt = $this->_conn->prepare("SELECT * FROM ". $this->table);
$stmt->execute();
return $stmt->fetchAll(PDO::FETCH_OBJ);
}
}
class Categorie extends Model
{
protected $table = 'categorie';
/**
* Le constructeur :
* va définir le nom de la colonne qui est la clé primaire de
* la table
*/
public function __construct()
{
$this->_pk = 'id';
}
}
$test = new Categorie();
$c = $test->getAll();
foreach($c as $a)
{
echo $a->cat . '<br>';
}
Regarde d'abord la classe Model : j'ai viré la propriété private $id et j'ai mis à la place protected $_pk (pour Primary Key). Note au passage que par convention, on préfixe très souvent les propriétés et les méthodes privées ou protégées par un « _ », donc $_pk au lieu de $pk.
Ensuite, regarde la classe Categorie : j'ajoute un constructeur dans lequel je définis le nom de la colonne de clé primaire : c'est pour ça que cette propriété de la classe parente est protected et non plus private.
Fatal error: Call to a member function prepare() on a non-object in :
$stmt = $this->_conn->prepare("SELECT * FROM ". $this->table);
et
$stmt = $this->_conn->prepare("SELECT * FROM ". $this->table ." WHERE ". $this->_pk ."=:id");
Dans ma base:
PRIMARY BTREE Oui Non id 45 A Non
Pourquoi $_pk, qu'est-ce qui fait qu'on mets $pk?
Ça sent typiquement l'erreur de «copier/coller/et utiliser sans réflexion » Et la question tendrait à montrer que tu as as sauté sur le code sans lire ce que j'ai précisé autour....
Je mets du code pour illustrer mon propos et il n'est jamais exclus que j'y glisse accidentellement une coquille. Je n'ai pas testé, j'ai juste modifié sur la base de ton code sans pour autant l'exécuter en local, l'idée,c'est de te présenter une autre approche.
Cependant, en revérifiant, je ne vois pas de coquille dans le code posté hier : et comme tu ne me montres qu'un petit extrait de ce que tu as fait, je ne peux pas davantage te guider vers une solution quelconque ou une explication de l'erreur.
À la rigueur, il pourrait manquer un élément dans le constructeur de la classe étendue :
class Categorie extends Model
{
protected $table = 'categorie';
/**
* Le constructeur :
* va définir le nom de la colonne qui est la clé primaire de
* la table
*/
public function __construct()
{
parent::__construct();
$this->_pk = 'id';
}
}
J'avais fait un c/c mais j'ai quand même passé en revenu.
Là, j'ai fais autrement en regardant les choses à modifier en réfléchissant.
public function __construct()
{
parent::__construct();
$this->_pk = 'id';
}
Si je comprends bien, le constructeur va chercher le constructeur parent?
PS si je commente $this->_pk = 'id'; ça marche quand même pourquoi?
C'est exactement ça.
Si je mets en commentaire
$this->_pk = 'id';
Ça va quand même afficher, y a une raison pourquoi ça pas d'incidence?
Ça ne devrait pas fonctionner, c'est une anomalie que je ne peux expliquer sans voir l'intégralité du code utilisé.
Mais c'est une simple question de logique. Essaye avec une table dont le nom de la clé primaire est franchement différent, ça devrait planter puisque la classe parente ne disposera pas de l'élément nécessaire à la construction de la requête.
Bonne Journée!
Ma dernière version intégrale:
<?php
define('USERNAME2','root');
define('PASSWORD2','');
define('DSN2',"mysql:host=localhost;dbname=bdd");
class pdo_mysql {
private static $_instance;
public static function &pdo_connection() {
if (!self::$_instance) {
try {
self::$_instance = new PDO(DSN2, USERNAME2, PASSWORD2);
self::$_instance->setAttribute(PDO::ATTR_PERSISTENT,true);
self::$_instance->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION);
} catch (PDOException $est) {
die("pdo connection error! ". $est->getMessage() ."<br/>");
}
}
return self::$_instance;
}
private function __construct() {
}
private function __clone() {
}
}
?>
<?php
require_once 'pdo_mysql.php';
abstract class Model extends pdo_mysql {
private $_conn;
protected $_pk;
public function __construct() {
$this->_conn = pdo_mysql::pdo_connection();
}
public function select($id) {
$stmt = $this->_conn->prepare("SELECT * FROM ". $this->table ." WHERE ". $this->_pk ."=:id");
$stmt->bindValue(':id', $id, PDO::PARAM_INT);
$stmt->execute();
$rows = $stmt->fetchAll(PDO::FETCH_OBJ);
return $rows;
}
public function getAll() {
$stmt = $this->_conn->prepare("SELECT * FROM ". $this->table);
$stmt->execute();
return $stmt->fetchAll(PDO::FETCH_OBJ);
}
}
class Categorie extends Model {
protected $table='categorie';
public function __construct()
{
parent::__construct();
//$this->_pk = 'id';
}
}
$test = new Categorie();
$c = $test->getAll();
foreach ($c as $a) {
echo $a->cat .'<br>';
}
?>
Merci!!
Mouais, ben ça confirme ce que je disais : il devrait y avoir une erreur.
Je relève une chose dans ta classe pdo_mysql : pourquoi « &pdo_connection() » au lieu de « pdo_connection() » ? À quoi te sert le « & » ?
Autre point, modifie légèrement la méthode select() ta classe Model comme ceci :
public function select($id)
{
$sql = "SELECT * ".
"FROM " . $this->table . " ".
"WHERE " . $this->_pk . "=:id";
echo("<pre>\n");
var_dump($sql);
echo("</pre>\n");
$stmt = $this->_conn->prepare($sql);
$stmt->bindValue(':id', $id, PDO::PARAM_INT);
$stmt->execute();
$rows = $stmt->fetchAll(PDO::FETCH_OBJ);
return $rows;
}
Ensuite, exécute à nouveau ton code et montre-moi ce qui s'affiche avec le var_dump.
J'ai vu deux choses.
J'avais choisi la fonction selectAll() pour faire sortir des données.
Donc, je ne pouvais pas voir mon erreur vu qu'il aurait fallu
que je prennent select() lors de l'instanciation.
Avec le var_dump on ne voit pas le id du WHERE.
Le perluète, je crois qu'il sert à rien. Mais, j'ai vu ça à quelque part.
Au finale, je dois enlever les //
Voilà,
- décommenter l'initialisation du nom de la colonne de clé primaire;
- Retirer l'esperluette devant le nom de la méthode.
Sur le premier point, je t'invite à garder dans un coin de ton esprit l'idée suivante, un exercice en quelque sorte : supposons que parmi tes tables, il existe une table relationnelle avec une clé primaire composite constituée de deux colonnes : comment devra-tu gérer ça de façon générique ?
Sur le second point, c'est une question de passage de valeur par référence. Fouille un peu Google sur le sujet « passage par référence », tu devrais trouver pas mal de littérature sur la question, mais en PHP5, ce n'est pratiquement plus utilisé que dans quelques très rares cas, on entre alors dans un niveau de programmation plus pointu. Note bien cependant que pour ce qui me concerne, je n'ai jamais utilisé ça et je n'ai jamais eu de problèmes ni besoin d'y recourir.
Je sais qu'une clé primaire assure l'unicité des enregistrements dans une table et une clé étrangère c'est quand tu fais une relation de deux tables.
table A:
id
table B:
id
table C:
id_A et id_B ça serait id_B quand on fait des jointures. Je suppose.
Y a pas beaucoup d'explication sur ce que c'est une clé primaire composite
dans google, j'utilise plus souvent ixquick par principe.
Dans wikipedia:
Une clé étrangère identifie une colonne ou un ensemble de colonnes d'une table comme référençant une colonne ou un ensemble de colonnes d'une autre table (la table référencée). Les colonnes de la table référencée doivent faire partie d'une contrainte de clé primaire ou d'une contrainte d'unicité. La contrainte de clé étrangère garantit que les valeurs de chaque ligne de la table référençant existent dans la table référencée : ainsi une ligne de la table référençant ne peut pas contenir un ensemble de valeurs qui n'existe pas dans la table référencée.
Je comprends à peine. Je ne visualise pas.
Ok, alors voyons d'abord le principe de base des jointures : je t'invite à lire ce tuto que j'avais publié il y a pas mal de temps sur PHPFrance.
Maintenant, supposons le même cas ouvrant des possibilités en plus. Imagine qu'un même numéro de téléphone (typiquement un numéro de téléphone fixe) puisse servir à plusieurs personnes. Pour maérialiser ça, on va devoir intercaler une table relationnelle entre une table listant les personnes et celle listant les numéros de téléphone. ça nous donne les schéma suivant :
+------------------+ +--------------+ +---------------+
| personnes | | pers_has_tel | | telephones |
+------------------+ +--------------+ +---------------+
| pers_id PK |----| pers_id PK |----| tel_id PK |
| pers_nom | | tel_id PK | | tel_numero |
| pers_prenom | +--------------+ | tel_type |
+------------------+ +---------------+
Comme tu peux voir, la table relationnelle a deux colonnes qui forment la clé primaire composite : ce sont toutes les deux des clés étrangères, chacune faisant référence à la ligne d'une autre table. L'unicité est assurée en ce sens qu'une paire de valeur ne peut exister qu'une seule et unique fois dans la table. Donc on peut trouver plusieurs fois le même identifiant de personne, mais à chque fois avec un identifiant de téléphone différent, et inversement, plusieurs fois le même identifiant de téléphone avec pour chacune un identifiant de personne différent.