8796 sujets

Développement web côté serveur, CMS

Bonjour !

Voilà, je viens d'attaquer le php objet, dans le cadre de mon stage.
Je code une application web dans laquelle j'ai besoin d'un calendrier en base de données, que j'ai déjà créé.
J'ai un soucis avec une classe : la classe Jour.
J'ai une méthode statique, qui est censée me renvoyer (sous forme de tableau) une liste des jours pour un mois et une année donnée.
Le soucis est que la méthode me renvoit un tableau vide, et je ne sais absolument pas pourquoi.

Je crois avoir identifié le problème, à l'aide d'alerts javascript. Problème qui semble se trouver au moment de l'instanciation des jours...
J'ai placé mon alert juste après l'instanciation de mon Jour, qui est censée m'afficher le numéro de ce dernier fraichement créé.

Je vous laisse le code de la classe complète, et une page de test.

- Classe



<?php
    require_once(".\Utils\Connexion.php");
    
    class Jour
    {
        //--Propriétés
        private $No;
        private $Nom;
        private $Mois;
        private $An;


        //--Constructeur
        public function  __construct()
        {
            $NbArgs = func_num_args();

            switch($NbArgs)
            {
                case 4:
                    $this->No = func_get_arg(0);
                    $this->Nom = func_get_arg(1);
                    $this->Mois = func_get_arg(2);
                    $this->An = func_get_arg(3);
                default:
                    $this->No = "";
                    $this->Nom = "";
                    $this->Mois = "";
                    $this->An = "";
                    break;
            }
        }


        //--Méthodes statiques
        public static function liste($Mois, $An)
        {
            $result = $GLOBALS["mysqli"]->query("SELECT * FROM JOUR WHERE moisjour = $Mois AND anjour = $An");
            $Jours = Array();
            if($result)
            {
                $row = $result->fetch_assoc();

                while($row)
                {
                    $Jour = new Jour($row["nojour"], $row["nomjour"], $row["moisjour"], $row["anjour"]);
                    echo '<script>alert("' . $Jour->getNo() . '")</script>';
                    $Jours[] = $Jour;
                    $row = $result->fetch_assoc();
                }
            }
            return($Jours);
        }
       
        
        //--Accesseurs
        public function getNo()
        {
            return $this->No;
        }

        public function setNo($Id)
        {
            $this->No = $Id;
        }

        public function getNom()
        {
            return $this->Nom;
        }

        public function setNom($Nom)
        {
            $this->Nom = $Nom;
        }
    }
?>







- Page Test



<?php
    require_once('ClassesMetiers.php');
?>
<!doctype html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>Test</title>
    </head>
    <body>

                <?php
                $Mois = 11;
                $An = 2012;
                $Jours = Jour::liste($Mois, $An);
                ?>
                <table>
                    
                    <tr>
                        <?php
                        foreach($Jours AS $jour)
                        {
                            echo "<td>";
                            echo $jour->getNo() + "</br>" + $jour->getNom();
                            echo "</td>";
                        }
                        ?>
                        
                    </tr>
                    
                </table>
    </body>
</html>





Voilà. Le soucis est que j'obtiens une liste de <td></td> remplie de 0 (NoJour) et de chaine vide (NomJour).
De plus, j'obtiens donc comme prévu une série d'alert javascript, qui sont elles-aussi vide.
Le soucis ne vient pas de la requète, puisqu'en ayant remplacé mes alerts par :
echo '<script>alert("' . $row["nojour"] . '")</script>';

là, mon numéro était bien renvoyé correctement.

J'ai vraiment du mal à saisir le problème.
Ne peut-on pas instancier un objet depuis une méthode de classe ?
Modifié par Shqdow (15 Feb 2011 - 10:43)
Qu'est-ce que la ligne
$row = $result->fetch_assoc();

vient faire dans la boucle while qui boucle justement sur la variable $row ? C'est un coup à se taper une boucle infinie ça Smiley langue

Ce qui expliquerait ton problème puise que en faisant ça tu boucle certainement sur le premier item de tes résultat dont l'id est probablement 0

$row = $result->fetch_assoc(); //Avancer le pointeur d'un enregistrement
 
while($row) //Tant que l'enregistrement n'est pas null
{
     $row = $result->fetch_assoc();  //Avancer le pointeur d'un enregistrement
}


Cette syntaxe est inhabituelle mais correcte, et donc non, la boucle n'est pas infinie. J'obtiens bien à chaque fois une liste de jours de la bonne taille selon le mois. Le soucis est que les objets jours sont vides.

moust a écrit :
Ce qui expliquerait ton problème puise que en faisant ça tu boucle certainement sur le premier item de tes résultat dont l'id est probablement 0

Shqdow a écrit :

Le soucis ne vient pas de la requète, puisqu'en ayant remplacé mes alerts par :
echo '<script>alert("' . $row[\'nojour\'] . '")<script>';
là, mon numéro était bien renvoyé correctement.


et de toute façon, même si c'était le cas, c'est bien pour ça que j'affiche le nom du jour dans ma page de test, ce nom ne serait de toute façon pas "".
Modifié par Shqdow (15 Feb 2011 - 11:11)
$GLOBALS["mysqli"]

Aïe, mes yeux.
Si tu veux obtenir un objet de manière unique dans ton script, ne le places pas en global : utilise un pattern Singleton.
Ton problème, d'ailleurs, d'instance statique se résout par un pattern factory. Exemple:

class Factory {
  public static function factory($args) {
    $cls = __CLASS__;
    return new $cls($args);
  }
}


Ton coup de vérification d'arguments à coup de switch, c'est un truc à ne pas voir ce qu'il se passe. Les arguments facultatifs, ça existe.

Je ne parle même pas du "debug" à coups d'alertes JS alors qu'un echo simple n'a tué personne.

Sinon, je plussoie : tu as un deuxième fetch_assoc. Et la syntaxe PDO n'est pas respectée (mais je suppose que tu es en surcouche...), techniquement de base, c'est $instance->fetch(PDO::FETCH_ASSOC). Je te conseille en passant le fetchAll, plus pratique, se fait hors d'une boucle.
Bonjour lpu8er, et d'abord merci de cette réponse, aussi complète qu'instructive.

Bon, à franchement parler, et comme énoncé, je débute le php-objet.

Je n'ai par conséquent pas tout compris dans ta réponse.

-Mysqli : La raison pour laquelle je l'ai mis en global, c'est que j'utilise l'objet dans toutes mes classes métier (j'en ai une bonne dizaine).

-Switch(NbArgs) : D'une part j'ignorais l'existence d'arguments facultatifs, d'autre part cette classe ci n'implémente que 2 constructeurs : J'ai copié le modèle de mes classes plus grosses, qui elles ont une bonne demie douzaine de constructeurs.

-Debug : Aucune excuse Smiley biggrin

-Problème posé : Après une succincte recherche google, je n'ai, à priori, pas bien compris le principe des pattern factory.


En fait pour la petite histoire, j'ai commencé le développement en couche et objet sous ASP.net, j'essaye aujourd'hui d'utiliser ces notions sous php 5.3, ce qui n'est pas chose aisée (et notamment à propos des sujets susnommés : surcharge de constructeur, méthodes de classes, debugging, etc...).

Pourrais-tu, si le coeur t'en dit, préciser, en ce qui concerne mon problème, et aussi à propos de la syntaxe PDO ?
Modifié par Shqdow (15 Feb 2011 - 11:54)
C'était mysqli, pas reconnu la patte. Ca ne change rien à l'avantage à utiliser un pattern singleton:
class MySQL extends mysqli {
  protected static $singleton = null;
  public static function get($host, $usr, $pwd='') {
    if(self::$singleton === null) {
      $cls = __CLASS__;
      self::$singleton = new $cls($host, $usr, $pwd);
    }
    return self::$singleton;
  }
}


Placer de la ressource (ou de l'objet qui contient de la ressource) en global, c'est toujours à éviter. L'astuce du statique en pattern singleton est bien plus propre.

Pour le nb d'args, dans ce cas oui, tu as à y gagner, en effet.

PDO est assez simple, si on se prend pas trop la tête avec la notion de DSN, et tout est dans la doc PHP. C'est de la connexion BD en approche objet, mais aussi en surcouche, donc en abstraction. Pratique si demain tu décides de judicieusement (pub gratuite : check) passer en postGre par exemple. Et surtout, PDO te fournit pléthore de méthodes utiles.
http://php.net/PDO

Et ton souci, en lui-même vient probablement du doublon fetch_assoc. Souci résolu, certes, en passant par un fetchAll de PDO, mais les conseils que je t'ai donné ne sont là que pour t'aider à éviter des situations inconfortables à l'avenir.
Bon ok, merci encore.

Donc si je résume et si j'ai bien compris.

- Mysqli : "Instancier en static" à l'aide d'un tel pattern permet de conserver un objet instancié, de sorte à ce qu'il soit réutilisé plus tard s'il a déjà été instancié, et donc d'y avoir accès depuis n'importe où sans passer par $GLOBALS ?

- PDO : J'avais cru comprendre que la syntaxe n'était pas aisée.
Ma syntaxe proposée est l'équivalent d'un

while($row = fetch_assoc)
{
     
}

décomposé. Syntaxe très présente et utilisée =/

Pour mon soucis, il intervient au moment de l'instanciation et non au moment de l'accès aux données puisque, je le répète, dans ma boucle :



$row = $result->fetch_assoc();

while($row)
{
    $Jour = new Jour($row["nojour"], $row["nomjour"], $row["moisjour"], $row["anjour"]);
    echo '<script>alert("' . $row[\'nojour\'] . '")</script>';//Accès aux données
    $Jours[] = $Jour;
    $row = $result->fetch_assoc();
}

M'affiche une série d'alertes avec les bons numéros des jours.


$row = $result->fetch_assoc();

while($row)
{
    $Jour = new Jour($row["nojour"], $row["nomjour"], $row["moisjour"], $row["anjour"]);
    echo '<script>alert("' . $Jour->getNo() . '")</script>';//Accès à l'objet
    $Jours[] = $Jour;
    $row = $result->fetch_assoc();
}

M'affiche une série d'alertes vides.

Le soucis vient donc du fait que $row["nojour"] != $Jour->getNo() après instanciation.
Cependant, $row["nojour"], aussi bizarre la syntaxe soit-elle, renvoi bel et bien un Int contenant le numéro de mon jour CORRECT.

Je viens de découvrir un truc encore plus bizarre : J'ai déplacé et modifié mon alert, je l'ai placé dans le constructeur :
        
public function  __construct()
        {
            $NbArgs = func_num_args();

            switch($NbArgs)
            {
                case 4:
                    $this->No = func_get_arg(0);
                    echo '<script>alert("' . func_get_arg(0) . '")</script>';
                    $this->Nom = func_get_arg(1);
                    $this->Mois = func_get_arg(2);
                    $this->An = func_get_arg(3);
                default:
                    $this->No = "";
                    $this->Nom = "";
                    $this->Mois = "";
                    $this->An = "";
                    break;
            }
        }


Et là, par contre, le script m'affiche une série d'alertes avec les bons numéros de mes jours...
ce qui signifie 2 choses :
- le constructeur est appelé correctement (l'objet est instancié correctement ?)
- le switch n'est pas la cause du problème et le script passe bien par "case 4:"
Modifié par Shqdow (15 Feb 2011 - 12:18)
Pour mysqli, oui. L'autre avantage c'est que l'appel au singleton (get) instanciera la connexion si besoin, ou la renverra simplement. Plus besoin de se demander "est-t'on connecté ?".

Pour PDO, la syntaxe est en réalité aussi simple, voire plus.
Ce n'est pas parce qu'une syntaxe se voit encore utilisée qu'il faut y répondre. L'API mysql_ reste très utilisée, alors qu'elle n'est plus fournie en standard. En PDO:
// $stmt est un PDOStatement rempli
// syntaxe 1 : while
while($line = $stmt->fetch(PDO::FETCH_ASSOC)) {
  // $line est un array associatif classique, une ligne de résultat
}
// syntaxe 2 : foreach+fetchAll
$lines = $stmt->fetchAll(PDO::FETCH_ASSOC);
// $line est un array d'arrays associatifs, tous les résultats



Pour ton souci:
echo $jour->getNo() + "</br>" + $jour->getNom();

Avec des "+", qui est l'opérateur d'addition et non de concaténation (c'est "."), php va forcément (enfin pas tout à fait, mais bref) te retourner 0...
Salut

a écrit :
Ca ne change rien à l'avantage à utiliser un pattern singleton:


çà me tellement fait plaisir de lire cette phrase sur ce forum... Smiley ravi C'est vrai, il serait temps de si mettre les gars Smiley cligne et ne pas passer à côté de la spl aussi, ce serait dommage.

a écrit :
-Mysqli : La raison pour laquelle je l'ai mis en global, c'est que j'utilise l'objet dans toutes mes classes métier (j'en ai une bonne dizaine).


Là désolé mais ce n'est pas une raison suffisante.
Coucou à tous !

Merci de vos réponses et précisions à propos des syntaxes PDO et des patterns.

Entre temps je me suis penché un peu dessus, et en effet, ces syntaxes montrent beaucoup d'avantages.

Pour ce qui est de mon soucis, j'ai trouvé le coeur du problème.


            switch($NbArgs) 
            { 
                case 4: 
                    $this->No = func_get_arg(0); 
                    $this->Nom = func_get_arg(1); 
                    $this->Mois = func_get_arg(2); 
                    $this->An = func_get_arg(3);
                    break;//<------------------------- !!!
                default: 
                    $this->No = ""; 
                    $this->Nom = ""; 
                    $this->Mois = ""; 
                    $this->An = ""; 
                    break; 
            }



Je vais donc vous plussoyer au sujet de l'utilisation des switch(NbArgs) au cas où quelqu'un viendrait à passer sur ce post :
L'utilisation de ces switchs est toujours à prendre en dernier recours car compliquée et donc dangereuse surtout dans des cas comme ça, et je suis bien placé pour en parler, venant de passer une après-midi à tourner retourner le problème dans tous les sens, et tout ça, à cause, d'un p**** de "break;" oublié !
Modifié par Shqdow (16 Feb 2011 - 09:50)
Et, ironiquement, le break est facultatif sur le default.

Mais en effet, oui. J'uis assez honteux d'être passé à coté.