2010-07-26 16 views
2

J'ai une classe MySQL singleton écrite en PHP. Son code est ci-dessous:Conflits de classes MySQL singleton avec fonction récursive (PHP)

class Database { 
    private $_result = NULL; 
    private $_link = NULL; 
    private $_config = array(); 
    private static $_instance = NULL; 

    // Return singleton instance of MySQL class 
    public static function getInstance(array $config = array()) { 
     if (self::$_instance === NULL) { 
      self::$_instance = new self($config); 
     } 

     return self::$_instance; 
    } 

    // Private constructor 
    private function __construct(array $config) { 
     if (count($config) < 4) { 
      throw new Exception('Invalid number of connection parameters'); 
     } 

     $this->_config = $config; 
    } 

    // Prevent cloning class instance 
    private function __clone() {} 

    // Connect to MySQL 
    private function connect() { 
     // Connect only once 
     static $connected = FALSE; 

     if ($connected === FALSE) { 
      list($host, $user, $password, $database) = $this->_config; 

      if ((!$this->_link = mysqli_connect($host, $user, $password, $database))) { 
       throw new Exception('Error connecting to MySQL : ' . mysqli_connect_error()); 
      } 

      $connected = TRUE; 

      unset($host, $user, $password, $database);  
     } 
    } 

    // Perform query 
    public function query($query) { 
     if (is_string($query) and !empty($query)) { 
      $this->connect(); 
      if ((!$this->_result = mysqli_query($this->_link, $query))) { 
       throw new Exception('Error performing query ' . $query . ' Message : ' . mysqli_error($this->_link)); 
      } 
     } 
    } 

    // Fetch row from result set 
    public function fetch() { 
     if ((!$row = mysqli_fetch_object($this->_result))) { 
      mysqli_free_result($this->_result); 
      return FALSE; 
     } 

     return $row; 
    } 

    // Get insertion ID 
    public function getInsertID() { 
     if ($this->_link !== NUlL) { 
      return mysqli_insert_id($this->_link); 
     } 

     return NULL; 
    } 

    // Count rows in result set 
    public function countRows() { 
     if ($this->_result !== NULL) { 
      return mysqli_num_rows($this->_result); 
     } 

     return 0; 
    } 

    // Close the database connection 
    function __destruct() { 
     is_resource($this->_link) AND mysqli_close($this->_link); 
    }  
} 

J'ai aussi cette fonction récursive qui doit retourne un arbre de catégorie complète (la table SQL et son contenu peut être trouvé here):

function getCategories($parent = 0) { 
    $html = '<ul>';  
    $query = "SELECT * FROM `categories` WHERE `category_parent` = '$parent'"; 
    $database->query($query);  
    while($row = $database->fetch()) { 
     $current_id = $row->category_id; 
     $html .= '<li>' . $row->category_name; 
     $has_sub = 0; 
     $query = "SELECT `category_parent` FROM `categories` WHERE `category_parent` = '$current_id'"; 
     $database->query($query); 
     $has_sub = $database->countRows(); 

     if ($has_sub > 0) {   
      $html .= getCategories($current_id); 
     } 

     $html .= '</li>'; 
    } 

    $html .= '</ul>'; 
    return $html; 
} 

Maintenant, le problème est que la fonction retourne seulement 3 catégories, pas l'arbre complet. J'ai réécrit la fonction en utilisant des fonctions MySQL simples (mysql_query(), mysql_fetch_object() etc.) et cela retourne le bon résultat.

Donc, ma conclusion en ce qu'il y a quelque chose de mal avec cette classe. Notez que j'ai utilisé cette classe dans la plupart de mes projets, mais je n'ai jamais eu ce problème.

Une idée quoi?

Merci.

EDIT: Essayer de faire retourner un tableau associatif

function getCategories($parent = 0) { 
    global $database; 
    $categories = array(); 

    $query = "SELECT * FROM `categories` WHERE `category_parent` = '$parent'"; 
    $database->query($query);  
    while($row = $database->fetch()) { 
     $categories[] = array('id' => $row->category_id, 'name' => $row->category_name);   
    } 

    for ($i = 0; $i < count($categories); $i++) { 

     $categories[$i]['id']['children'] = getCategories($categories[$i]['id']); 

    } 

    return $categories; 
} 

Le code ci-dessus renvoie le tableau suivant, mais pas tout à fait correct:

Array 
(
    [0] => Array 
     (
      [id] => 1 
      [name] => Categoria 1 
      [children] => Array 
       (
        [0] => Array 
         (
          [id] => 4 
          [name] => Categoria 1.1 
          [children] => Array 
           (
            [0] => Array 
             (
              [id] => 7 
              [name] => Categoria 1.1.2 
              [children] => Array 
               (
               ) 

             ) 

           ) 

         ) 

        [1] => Array 
         (
          [id] => 5 
          [name] => Categoria 1.2 
          [children] => Array 
           (
           ) 

         ) 

        [2] => Array 
         (
          [id] => 6 
          [name] => Categoria 1.3 
          [children] => Array 
           (
           ) 

         ) 

       ) 

     ) 

    [1] => Array 
     (
      [id] => 2 
      [name] => Categoria 2 
      [children] => Array 
       (
       ) 

     ) 

    [2] => Array 
     (
      [id] => 3 
      [name] => Categoria 3 
      [children] => Array 
       (
       ) 

     ) 
) 
+1

Est-ce le code complet de 'getCategories'? Ne devrait-il pas y avoir un appel '$ database = Database :: getInstance()'? –

+0

Vous faites beaucoup de réinvention de la roue ici. Avez-vous envisagé d'utiliser [mysqli en mode objet] (http://us2.php.net/manual/fr/class.mysqli.php) au lieu du mode procédural et d'étendre simplement les classes pour obtenir toutes les fonctions de commodité que vous désirez? Ou en utilisant [PDO] (http://us2.php.net/manual/fr/book.pdo.php) et en faisant la même chose? – Charles

+0

@George Marian: oui, cet appel est là dans le script, mais je ne l'ai pas posté ici. – Psyche

Répondre

3

Le problème que vous » Le fait de voir est que vous exécutez d'autres requêtes sur votre première requête. Ainsi, après avoir parcouru la première branche, la fonction échoue car elle retourne à la boucle originale which et trouve qu'elle est déjà à la fin des lignes résultantes de la première branche (3 niveaux de récursion).

Une solution rapide serait de réécrire votre fonction en utilisant deux boucles au lieu d'une seule. Vous pouvez également optimiser la fonction de cette manière en réduisant le nombre d'appels DB que vous devez effectuer.

Votre première requête est correcte. Mais dans votre boucle while, il suffit de saisir les éléments id et name appropriés et de les stocker dans un tableau. Puis reloop à travers le tableau que vous venez de créer pour recurse. En outre, vous pouvez éliminer plusieurs requêtes en n'exécutant pas une requête "vérifier". Recurse juste - et s'il n'y a aucun élément le tableau id: name - retourne une chaîne vide.

EDIT: un exemple (non testé)

function getCategories($parent = 0) { 
    $categories = array(); 
    $html = "<ul>"; 
    $query = "SELECT * FROM `categories` WHERE `category_parent` = '$parent'"; 
    $database->query($query);  
    while($row = $database->fetch()) { 
     $categories[$row->category_id] = $row->category_name; 
    } 
    foreach($categories as $cid=>$category) { 
     $html .= "<li>{$row->category_name}"; 
     $inner = getCategories($cid); 
     if($inner != "<ul></ul>") 
      $html .= $inner; 
     $html .= "</li>" 
    } 
    $html .= "</ul>"; 
    return $html; 
} 
+0

@thetaiko: Je comprends ce que vous dites, mais je ne suis pas sûr d'écrire les changements. Pouvez-vous donner un exemple s'il vous plaît? – Psyche

+0

@ Psyche- voir ma critique éditée. Je n'ai pas testé cela et il peut y avoir des erreurs, mais cela véhicule l'idée générale. En outre, je n'aime généralement pas mélanger le HTML dans mon PHP, mais l'ai fait dans l'intérêt de cet exemple. Vous pourriez développer ceci pour exécuter une autre requête qui vérifierait des sous-catégories pour toutes les clefs dans '$ categories' à la fois, mais j'ai omis cela aussi bien pour le garder simple. – thetaiko

+0

@thetaiko: il y avait une petite erreur, mais ça va maintenant. Ça marche! Je n'aime pas non plus mélanger le HTML en PHP, donc je pensais à le retourner en tant que tableau associatif et ensuite l'afficher avec Smarty. – Psyche

0

Votre problème de base est que votre base de données objet est schizophrène: il est à la fois un objet de connexion et un objet de ressource. Les requêtes supplémentaires effectuées avant la fin de la précédente écraseront le descripteur de ressource existant.

La solution est simple: il suffit de créer une seconde classe qui encapsule les poignées de ressources et renvoie les données.