8792 sujets

Développement web côté serveur, CMS

Bonjour à tous,

Je suis en train de développer une classe pour gérer les menus, tout avec PDO.

Bref quand je lance ma fonction create_menu('0') depuis mon objet.

J'ai dès la première recursivité de mon code, une erreur de ce type

Fatal error: Call to a member function fetch() on a non-object in class menu.php on line 98


Voici mon code

SI vous avez une idée, je suis preneur !

Merci d'avance


<?php

class Menu {

public $db;						// base de données - class d'abstraction PDO
public $table;					// table contenant les informations du menu


	function __construct($z_table,$z_db){
		$this->db 		= $z_db;
		$this->table 	= $z_table;
	}

   	/**
   	 * Menu::get_title()
   	 *
   	 * @param mixed $id
   	 * @return - string - donne le title pour $id
   	 **/
   	function get_title($id){
   		$title	= $this->db->query("SELECT title FROM $this->table
		   							WHERE id = '$id'");
   		$result = $title->fetch(PDO::FETCH_BOTH);
      	return $result[0];
   	}

   	/**
   	 * Menu::get_parentID()
   	 *
   	 * @param mixed $id
   	 * @return - string - donne le parentID pour $id
   	 **/
   	function get_parentID($id){
   		$parentID	= $this->db->query("SELECT parentID FROM $this->table
		   							WHERE id = '$id'");
   		$result 	= $parentID->fetch(PDO::FETCH_BOTH);
      	return $result[0];
   	}

	/**
	 * Menu::create_node()
	 *
	 * @param mixed $title
	 * @param mixed $position
	 * @param mixed $parentID
	 * @return
	 **/
	function create_node($title,$position,$parentID){
		$this->db->query("INSERT INTO $this->table(title,position,parentID)
							VALUES ('$title','$position','$parentID')");
	}

	/**
	 * Menu::delete_node()
	 *
	 * @param mixed $id
	 * @return
	 **/
	function delete_node($id){
		$this->db->query("DELETE FROM $this->table WHERE id = '$id'");
	}

	/**
	 * Menu::update_node()
	 *
	 * @param mixed $id
	 * @param mixed $title
	 * @param mixed $position
	 * @param mixed $parentID
	 * @return
	 **/
	function update_node($id,$title,$position,$parentID){
		$this->db->query("UPDATE $this->table SET
							title		=	'$title',
							position	=	'$position',
							parentID	= 	'$parentID'
							WHERE id	=	'$id';");
	}

	function create_menu($parentID){
		global $i;
		global $req;

		if ($parentID == 0) {$i=0;}
		$i = $i + 1 ;

   		$req = $this->db->query("SELECT * FROM $this->table
		   							WHERE parentID = '$parentID'");

		print "<ul id=\"menu\">\n";

		while($myrow  = $req->fetch()) {
			print '<li><a href="'.$myrow['id'].'">'.$myrow['title'].'</a>';
			$this->create_menu($myrow['id']);
			print "</li>\n";
		}
		print "</ul>\n";

	}

}
?>


Et pour excuter ce code :
<?php
// paramètres de connexion
$dsn = 'mysql:host=localhost;dbname=mabase';
$user = 'utilisateur';
$password = 'motsdepasse';
$db = new PDO($dsn, $user, $password);

$menu = new Menu('matable', $db);

$menu->create_menu('0');
?> 


Le problème vient clairement de la fonction create_menu mais je vois pas pourquoi, dès lors qu'il y a récursivité, cela ne marche plus (sauf la première execution et non les executions de recursivité)
Modifié par neopheus (08 Jul 2011 - 00:15)
Bonjour.

Vous ne faites aucune vérification sur les résultats des requêtes... Donc, dans les cas où la requête ne s'effectue pas correctement, l'objet renvoyé est inconsistant. (par exemple : false, ou null, je ne sait pas...)
Il convient donc de vérifier si la construction de PDO s'est bien effectuée, et vérifier à chaque requête que celle-ci renvoie quelque-chose d'exploitable...
GeorgesM a écrit :
Bonjour.

Vous ne faites aucune vérification sur les résultats des requêtes... Donc, dans les cas où la requête ne s'effectue pas correctement, l'objet renvoyé est inconsistant. (par exemple : false, ou null, je ne sait pas...)
Il convient donc de vérifier si la construction de PDO s'est bien effectuée, et vérifier à chaque requête que celle-ci renvoie quelque-chose d'exploitable...


Concretement sur cette class, il faut l'implenter comment ?
Il faut tout vérifier à chaque étape.

- Est-ce que PDO est bien initialisé,
- Est-ce que les enregistrements des menus se sont bien effectués,
- Est-ce qu'on lit effectivement les enregistrements désirés, etc.. etc...

De plus, si l'auteur est Xavier VALENTIN, peut-être pouvez-vous le contacter...
http://www.developpez.net/forums/member.php?u=84896
Modifié par GeorgesM (11 Nov 2006 - 09:26)
Concernant PDO, les requetes fonctionnent, et qd il s'agit d'enregistrements simple sans recursivité : ça marche impeccable

Mais dès qu'il y a de la récursivité ça plantre :s
Modifié par neopheus (08 Jul 2011 - 00:16)
Je viens de faire une maquette de votre application en utilisant sqlite à la place de MySql. (pratique, PDO) Smiley smile

Je n'obtiens le message d'erreur que si la table est mal initialisée...

Si la table est OK, j'obtiens l'affichage d'un menu et de sous menu en désactivant la ligne

	//global $req; [eek] 

Pourquoi global ?

Donc, à mon avis, il faudrait déclencher une exception à la construction si l'objet PDO n'est pas initialisé correctement...

A part ça, ça fonctionne. Pas de soucis récursif. Mais il faut durcir le code...
Modifié par GeorgesM (12 Nov 2006 - 13:30)
A mon humble, avis, tant qu'à utiliser PDO, autant l'utiliser avec ses fonctionnalités de débogages....

Utilisons la fonction exec pour exécuter les manipulations, et lisons les messages d'erreurs :


$db = new PDO($dsn, $login, $mdp);

// comme dans mon cas, c'est une bd sqlite, j'ai une syntaxe différente
$rqt="
CREATE TABLE 'nodes' (
  'id' int(11) NOT NULL AUTOINCREMENT,
  'title' varchar(255) NOT NULL,
  'position' int(11) NOT NULL,
  'parentID' int(11) NOT NULL,
  PRIMARY KEY  ('id')
);
";

if (!$db->exec($rqt))
{
	foreach($db->errorInfo() as $item)
	echo "<div>$item</div>";
}

$rqt="
INSERT INTO nodes VALUES (1, 'root', 1, 0);
INSERT INTO nodes VALUES (2, 'europe', 2, 1);
INSERT INTO nodes VALUES (3, 'norway', 3, 2);
INSERT INTO nodes VALUES (4, 'ste', 4, 3);
INSERT INTO nodes VALUES (5, 'oslo', 5, 3);
INSERT INTO nodes VALUES (6, 'test', 6, 0);
";

if (!$db->exec($rqt))
{
	foreach($db->errorInfo() as $item)
	echo "<div>$item</div>";
}



Ainsi, dans le cas où quelque chose ne va pas, on peut lire les messages d'erreurs.

Toujours avec PDO, un petit dump ne ferait pas de mal, pour fixer les idées :


$result = $db->query("SELECT * FROM nodes");
while($myrow  = $result->fetch())
{
	echo "<div>{$myrow['id']} {$myrow['title']} {$myrow['position']} {$myrow['parentID']}</div>";
}

doit afficher :
1 root 1 0
2 europe 2 1
3 norway 3 2
4 ste 4 3
5 oslo 5 3
6 test 6 0

Arrivé à ce point, plus rien n'empeche le script de fonctionner, et de mon côté, j'obtiens avec $menu->create_menu('0');

   root
      europe
         norway
            ste
            oslo
   test
Je comprend bien ce que tu dis.

Mais le problème que j'ai c'est de pouvoir avoir ma structure en <ul> - <li> avec autant de sous-niveaux

Pour se faire j'ai mis une fonction récursive que tu retrouves dans : function create_menu($parentID) :

Et plus précisement :

		while($myrow  = $req->fetch()) {

			print '<li><a href="'.$myrow['id'].'">'.$myrow['title'].'</a>';

			$this->create_menu($myrow['id']);

			print "</li>\n";

		}


Si je désactive la récursivité en mettant : $this->create_menu($myrow['id']); en commentaire ou en le supprimant, celà fonctionne pour la boucle parent :

root
test


Je dois certainement m'y prendre comme un manche avec PDO.

Mais j'arrive pas à avoir cette récursivité pour avoir ce résultat en sorti :

<ul>
  <li>Root
    <ul>
      <li>Europe
        <ul>
          <li>Norway
            <ul>
              <li>Ste</li>
              <li>Oslo</li>
            </ul>
          </li>
        </ul>
      </li>
    </ul>
  </li>
  <li>test</li>
</ul>
Puisque vous obtenez root et test en désactivant la récursivité, c'est que votre base de donnée est OK. C'est déjà un point dont on est sur.

A ce stade, je pense que c'est peut-être un bug du driver PDO MySql. Je pense celà parce que de mon côté, en utilisant sqlite, celà fonctionne.

Pour nous en convaincre, peut-être devriez-vous initialiser PDO en sqlite ?
Avant celà, vérifier avec phpinfo() que les drivers sqlite sont disponibles.

PDO support enabled
PDO drivers sqlite2


// Pour SQLite
$dsn   = 'sqlite2:testdb.sq3';
$login = '';
$mdp   = '';
$db = new PDO($dsn, $login, $mdp);


Enfin, quelle version de PHP avez-vous?


http://bugs.php.net/bug.php?id=33724
Ce lien est interessant. On peut y lire :

a écrit :

Thank you for taking the time to write to us, but this is not
a bug. Please double-check the documentation available at
http://www.php.net/manual/ and the instructions on how to report
a bug at http://bugs.php.net/how-to-report.php

ATM parallel queries are not available using mysql. Wait for someone to
write a decent pdo_mysql driver, use a decent rdbms like postgres or use
fetchall().


et plus loin :
a écrit :

FYI, you should use fetchAll().
The PDO MYSQL driver *is* decent, and what you're trying to do is not
supported by most major databases.


... ce qui me fait penser qu'il y a une limitation technique avec MySql et la récursivité...
Modifié par GeorgesM (13 Nov 2006 - 13:03)
Concernant PHP j'ai la Version 5.1.6
PDO drivers : mssql, mysql, sqlite2

Si je comprend rien avec PDO et son support mysql, il est impossible de faire de requetes récursives.... Smiley bawling

Alors comment on va faire ? Smiley sweatdrop
Objectivement, est-ce que sqlite ne ferait pas l'affaire ? De mon côté, ça fonctionne.

Sinon, il reste à tester le code modifié comme suit :

	function create_menu($parentID)
	{
		global $i;
		$s ="";
		if ($parentID == 0) {$i=0;}
		$i = $i + 1 ;

   		$req = $this->db->query("SELECT * FROM $this->table	WHERE parentID = '$parentID'");

		$s .= "<ul id=\"menu\">\n";

		$result = $req->fetchAll();[winner] 

		foreach ($result as $myrow)
		{
			$s .= '<li><a href="'.$myrow['id'].'">'.$myrow['title'].'</a>';
			$s .= $this->create_menu($myrow['id']);
			$s .= "</li>\n";
		}

		$s .= "</ul>\n";
		return $s;
	}


.. c'est à dire qu'en substituant fetchall à fetch, on devrait contourner la limitation de mysql car on ne lance pas plusieurs requêtes parallèles...

Note: conformément à mes habitudes, je ne fais pas de print dans les objets. Je construit une chaîne destinée à être affiché par echo au bon moment.


$menu = new Menu('nodes', $db);
echo $menu->create_menu('1');

Modifié par GeorgesM (13 Nov 2006 - 15:00)
GeorgesM a écrit :
Objectivement, est-ce que sqlite ne ferait pas l'affaire ? De mon côté, ça fonctionne.

Sinon, il reste à tester le code modifié comme suit :

	function create_menu($parentID)
	{
		global $i;
		$s ="";
		if ($parentID == 0) {$i=0;}
		$i = $i + 1 ;

   		$req = $this->db->query("SELECT * FROM $this->table	WHERE parentID = '$parentID'");

		$s .= "<ul id=\"menu\">\n";

		$result = $req->fetchAll();[winner] 

		foreach ($result as $myrow)
		{
			$s .= '<li><a href="'.$myrow['id'].'">'.$myrow['title'].'</a>';
			$s .= $this->create_menu($myrow['id']);
			$s .= "</li>\n";
		}

		$s .= "</ul>\n";
		return $s;
	}


.. c'est à dire qu'en substituant fetchall à fetch, on devrait contourner la limitation de mysql car on ne lance pas plusieurs requêtes parallèles...

Note: conformément à mes habitudes, je ne fais pas de print dans les objets. Je construit une chaîne destinée à être affiché par echo au bon moment.


$menu = new Menu('nodes', $db);
echo $menu->create_menu('1');


Oulalalala ça ne fait pas d'erreur Smiley lol

C'est casiment parfait Smiley smile

Alors je te tire mon chapeau, et je te remercie mille fois. Smiley biggrin

Mais le code généré a quelques erreurs par rapport au résultat demandé Smiley bawling .

Là ça doit venir d'un problème dans mon algo (mais pourquoi le monde est si cruel avec moi ?)
    <ul id="menu">
      <li>
      <a href="1">root</a>
      <ul id="menu">
        <li>
        <a href="2">europe</a>
        <ul id="menu">
          <li>
          <a href="3">norway</a>
          <ul id="menu">
            <li>
            <a href="4">ste</a>
            <ul id="menu">
            </ul></li>
            <li>
            <a href="5">oslo</a>
            <ul id="menu">
            </ul></li>
          </ul></li>
        </ul></li>
      </ul></li>
      <li>
      <a href="6">test</a>
      <ul id="menu">
      </ul></li>
    </ul>

Modifié par neopheus (13 Nov 2006 - 18:43)
En fait, j'ai appris plein de choses sur le driver MySql de PDO... et ses limitations.

Maintenant que la fonction récursive fonctionne, c'est à vous de paufiner le code pile poil...
GeorgesM a écrit :
En fait, j'ai appris plein de choses sur le driver MySql de PDO... et ses limitations.

Maintenant que la fonction récursive fonctionne, c'est à vous de paufiner le code pile poil...


Là je suis à fond dessus Smiley smile

Voici le code que j'obtiens avec le code que j'ai fait ci-dessus (code nettoyé):

    <ul>
      <li>root
        <ul>
          <li>europe
            <ul>
              <li>norway
                <ul>
                  <li>ste
                  <ul>
                  </ul>
                  </li>
                  <li>oslo
                  <ul>
                  </ul>
                  </li>
                </ul>
              </li>
            </ul>
          </li>
        </ul>
      </li>
      <li>test
        <ul>
        </ul>
      </li>
    </ul>


Et là on se rend facilement compte qu'il s'agit juste d'un p'tit pb de balise vide, je corrige ça de suite Smiley smile

Et je tiens à te remercier pour ton aide

C'est super sympa Smiley smile
Modifié par neopheus (13 Nov 2006 - 19:00)