tuto POO en PHP / Chapitre 12 : SetFetchMode

Par Julien Brault, il y a 9 ans


Bonjour,

Voila je rencontre un petit problème avec mon code.
Dans le Tuto, dans le chapitre 12, a partir du temps de la video 35mn15sec, on a une erreur :
Le resultat de notre requette n'est pas un OBJET mais un TABLEAU
Tout cela est resolu de 35mn15sec à 37mn12sec

Je fais exactement la meme chose, mais à la fin, ma variable $post est toujours considérée comme un tableau et non comme un objet

Est ce que quelqu'un saurait d'ou ca peut venir ? Ca fait 2 heures que je cherche sans resultat :-(

Merci, et merci au createur de ce site !!

20 réponses

SLK, il y a 9 ans

Ah oui, zut.

Mets des echo pour voir si ça correpsond bien à ce qu'on attend.

public function prepare($statement, $attributes, $class_name, $one = false) { $req = $this->getPDO()->prepare($statement); if ($class_name === null) { echo 'setFetchMode --- OBJ<br>'; $req->setFetchMode(PDO::FETCH_OBJ); } else { echo 'setFetchMode --- CLASS : ' . $class_name . '<br>'; $req->setFetchMode(PDO::FETCH_CLASS, $class_name); } $req->execute($attributes); if ($one) { $datas = $req->fetch(); } else { $datas = $req->fetchAll(); } return $datas; }

Et est-ce qu'on peut voir l'intégralité du fichier App/Table/Article.php ?

Julien Brault, il y a 9 ans

Ton message s'est croisé avec mon edit ;-)

l'intégralité du fichier App/Table/Article.php est consultable sur mmon message édité

Carouge10, il y a 9 ans

Bonsoir,
Il nous faudrait le code, au moins la classe Database.

Julien Brault, il y a 9 ans

Bonsoir,
Oui, voici le code de mon côté :

Il y a 3 fichiers

  • Database.php (class)
  • single.php (affiche l'article de blog)
  • index.php

Database.php

<?php namespace App; use \PDO; class Database{ private $db_name; private $db_user; private $db_pass; private $db_host; private $pdo; public function __construct($db_name, $db_user='root', $db_pass='', $db_host='localhost'){ $this->db_name=$db_name; $this->db_user=$db_user; $this->db_pass=$db_pass; $this->db_host=$db_host; } private function getPDO(){ if($this->pdo === null){ $pdo = new PDO('mysql:dbname=blog;host=localhost','root',''); $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $this->pdo = $pdo; } return $pdo; } public function query($statement, $class_name){ $req = $this->getPDO()->query($statement); $datas = $req->fetchAll(PDO::FETCH_CLASS, $class_name); return $datas; } public function prepare($statement, $attributes, $class_name, $one = false){ $req = $this->getPDO()->prepare($statement); $req->execute($attributes); $req->setFetchMode(PDO::FETCH_CLASS, $class_name); if($one) { $datas = $req->fetch(); } else { $datas = $req->fetchAll(); } return $datas; } }

single.php

<?php $post = $db->prepare('SELECT * FROM articles WHERE id = ?', [$_GET['id']], 'APP\Table\Article', true); var_dump($post); ?> <h1><?= $post->titre; ?></h1> <p><?= $post->contenu; ?></p>

index.php

<?php require '../app/Autoloader.php'; App\Autoloader::register(); if(isset($_GET['p'])){ $p = $_GET['p']; } else{ $p = 'home'; } // initialisation des objets $db = new App\Database('blog'); ob_start(); if($p === 'home'){ require '../pages/home.php'; } elseif ($p ==='article'){ require '../pages/single.php'; } $content = ob_get_clean(); require '../pages/templates/default.php';

$req->setFetchMode(PDO::FETCH_CLASS, $class_name); se trouve dans la class Database.php

SLK, il y a 9 ans

Salut,

Il y a une petite erreur dans getPDO() :

la 1ère fois qu'on fait un getPDO(),
$this->pdo est null, donc on créer $pdo, on le "range" dans $this->pdo, et on le retourne.
Ça fonctionne... Mais,
la 2ème fois qu'on fait un getPDO(),
this->pdo n'est PLUS null, donc on ne créer PAS $pdo, pourtant on essaie de le "retourner" à la fin. Absurde

Ce n'est pas $pdo qu'il faut retourner, mais $this->pdo;

Julien Brault, il y a 9 ans

Salut,

Merci pour ta remarque constructive! Effectivement, je ne faisais pas le bon return. Je viens de corriger cette partie.

Malgré celà, j'ai quand meme le même problème :

$post est considéré comme un "Array" plutot qu'un "object"

Voici un screenshot de la page Article dans le navigateur (issue de single.php) : http://jbrault.esy.es/php.jpg

Tapion, il y a 9 ans

Dans ta fonction prepare il faut que tu modifies un paramètre

EDIT : bon un codeshare.io car y'a un bug de syntaxe

https://codeshare.io/5Mjj1Q

Julien Brault, il y a 9 ans

Salut,

Merci pour l'amélioration du code, mais j'ai encore la même erreur

Database.php :

<?php namespace App; use \PDO; class Database{ private $db_name; private $db_user; private $db_pass; private $db_host; private $pdo; public function __construct($db_name, $db_user='root', $db_pass='', $db_host='localhost'){ $this->db_name=$db_name; $this->db_user=$db_user; $this->db_pass=$db_pass; $this->db_host=$db_host; } private function getPDO(){ if($this->pdo === null){ $pdo = new PDO('mysql:dbname=blog;host=localhost','root',''); $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $this->pdo = $pdo; } return $this->pdo; } public function query($statement, $class_name){ $req = $this->getPDO()->query($statement); $datas = $req->fetchAll(PDO::FETCH_CLASS, $class_name); return $datas; } public function prepare($statement, $attributes, $class_name, $one = false){ $req = $this->getPDO()->prepare($statement); $req->execute($attributes); if($class_name === null){ $req->setFetchMode(PDO::FETCH_OBJ); }else { $req->setFetchMode(PDO::FETCH_CLASS, $class_name); } if($one) { $datas = $req->fetch(); } else { $datas = $req->fetchAll(); } return $datas; } }
Tapion, il y a 9 ans

Tu as quoi dans le var_dump ?

je t'ai mis un codeshare à la place à cause d'un bug de syntaxe

Tapion, il y a 9 ans

Oui la c'est normal car $class_name c'est pas null du coup tu es toujours en fetch_class

SLK, il y a 9 ans

Je n'en suis pas sûr mais,
peut-être qu'il faut faire le execute après le setFetchMode.
Et toujours avant le fetch/fetchAll bien sûr.

Ça paraitrai logique, mais si ça se trouve PDO se débrouille pour que ça fonctionne quand même, qu'on le fasse avant ou après, je ne sais pas.
En tous cas dans le doute, essaie pour voir ?

public function prepare($statement, $attributes, $class_name, $one = false) { $req = $this->getPDO()->prepare($statement); if ($class_name === null) { $req->setFetchMode(PDO::FETCH_OBJ); } else { $req->setFetchMode(PDO::FETCH_CLASS, $class_name); } $req->execute($attributes); if ($one) { $datas = $req->fetch(); } else { $datas = $req->fetchAll(); } return $datas; }
Julien Brault, il y a 9 ans

Ca ne marche pas non plus.

Sincèrement, merci pour vos idées et vos efforts déployés pour trouver une solution, et malheureusement, je crainds etre confronté à un bug vraiment ch*** à trouver

SLK, il y a 9 ans

Dans "single.php" quand tu fais ça :

$post = $db->prepare('SELECT * FROM articles WHERE id = ?', [$_GET['id']], 'APP\Table\Article', true);

Le paramètre passé pour la "class" est : 'APP\Table\Article'.
Ça me parait étrange de passer une classe de "table", ça ne devrait pas être une classe "d'entité" plutôt ?

Julien Brault, il y a 9 ans

Ca vient d'ici, à la 35eme minute :
https://www.grafikart.fr/formations/programmation-objet-php/tp-database

Et voici le code de la classe Article rangée dans App\Table :
Article.php

<?php namespace APP\Table; class Article{ public function __get($key){ $method = 'get' . ucfirst($key); $this->$key = $this->$method(); return $this->$key; } public function getURL(){ return 'index.php?p=article&id=' . $this->id; } public function getExtrait(){ $html ='<p>' . substr($this->contenu, 0, 200) . ' ...</p>'; $html .= '<p><a href="' . $this->getURL() . '">Voir la suite</a></p>'; return $html; } }
SLK, il y a 9 ans

D'accord,

bon alors les echo nous montrent bien qu'on obtient une "class", c'est ce qu'on voulait c'est bon.

Je me pose quand même la question :
Est-ce qu'on peut laisser "APP/...", ou on devrait respecter la casse ? Avec "App/..."

En tous cas,
En fait je ne comprend pas... Mais je laisse quand même ma "réflexion" ici, je ne sais pas, peut-être que ça pourra te faire penser à quelquechose qui t'aidera...

On obtient donc une class App/Table/Article,
et cette classe n'a aucun attribut (seulement des méthodes).
Pourtant on essaie d'accéder à des attributs :

<h1><?= $post->titre; ?></h1>

Du coup la méthode magique est appelée.

public function __get($key) { $method = 'get' . ucfirst($key); $this->$key = $this->$method(); return $this->$key; }

Dans cette méthode, on fait : $this->$key = ...,
mais $this->$key n'existe pas (la classe n'a aucun attribut). Absurde

Ça parait louche, mais finalement, ça ne me semble même pas pertinent de mentionner ça parce-que :
le message d'erreur ne dis pas : "on essaie d'accéder à un attribut qui n'existe pas",
il dit : "on essaie d'accéder à un attribut sur quelque chose qui n'est pas un objet".

Pourtant, on passe bien dans le $req->setFetchMode(PDO::FETCH_CLASS, $class_name);,
et j'étais convaincu que ça nous donnerai un "objet" à la fin sur $datas,
pourtant quand on fait un var_dump sur ce qu'a retourné la fonction (la fonction "prepare"),
on a un "tableau"... :'(
Fais un var_dump() juste avant le return, dans la fonction "prepare" pour voir ? (mais je commence à me dire qu'on s'éloigne de la solution...)

... if ($one) { $datas = $req->fetch(); } else { $datas = $req->fetchAll(); } var_dump($datas); // ici, on voit un "object" ou un "array" ??? return $datas; }
Julien Brault, il y a 9 ans

oui, tu résumes parfaitement bien la problème qui semble absurde...

Pour le var_dump($data), placéà la fin de la méthode prepare(), ca retourne bien un array plutot qu'un objet :-(

Julien Brault, il y a 9 ans

Ohhhh je viens de trouver

C'était un problème de casse

single.php - mauvais

$post = $db->prepare('SELECT * FROM articles WHERE id = ?', [$_GET['id']], 'APP\Table\Article', true);

single.php - corrigé

$post = $db->prepare('SELECT * FROM articles WHERE id = ?', [$_GET['id']], 'App\Table\Article', true);

Et il fallait appeller le namespace de cette facon : namespace App\Table; plutôt que namespace APP\Table;

Vraiment, merci pour ton aide, ton message d'il y a 2 heures, quand tu parlais du single.php, et ton message sur la casse, m'ont mis sur la piste.
Génial! :-)

SLK, il y a 9 ans

Ah ben super ^^

Et du coup je me rend compte que je me suis bien embrouillé...
Quand on fait ça :

<h1><?= $post->titre; ?></h1>

On essaie pas d'accéder à l'attribut "titre" d'un objet instancié par la classe App/Table/Article, non.
On essaie d'accéder au champ "titre" sélectionné par la requête SQL.

Bref... ^^