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


Julien Brault
Auteur
Réponse acceptée

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! :-)

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

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

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;

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

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

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;
    }
}

Tu as quoi dans le var_dump ?

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

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

Merci pour le codeshare
Dans le var_dump, ca me donne toujours un array :

http://jbrault.esy.es/php.jpg

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;
}

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

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 ?

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;
    }
}

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 ?

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

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

Et avec les echo, ca donne ca :

http://jbrault.esy.es/php2.jpg

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;
}

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 :-(

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... ^^