2009-05-08 13 views
2

j'ai une table dans une base de données qui a la catégorie des données:PHP SPL pour manipuler le menu récursif

 
id title  parent 
1 category 1 0 
2 category 2 2 
3 category 3 3 
4 category 4 0 

parent cChaque peut avoir l'id de la ligne parent.

Par exemple, la catégorie 3 est un enfant de la catégorie 2, qui est enfant de la catégorie 1.

 
category 1 
    category 2 
     category 3 

category 4 

1 - Y at-il une meilleure façon de manipuler cette technique? 2 - Mon implémentation consiste à récupérer toutes les lignes d'une requête SQL, puis à utiliser une fonction récursive pour construire un tableau multi-dimensionnel, puis à le parcourir en utilisant multi level foreach pour construire un menu ou autre. J'ai besoin d'un meilleur moyen d'utiliser PHP SPL.

Je veux créer soit un menu complet ou un chemin d'élément comme:

 
category1 -> catgory 2 -> etc. 

et créer une grille qui maintient le niveau de la catégorie dans chaque ligne.

Répondre

1

J'ai récemment dôme quelque chose de similaire en utilisant une seule requête et une seule boucle while. Il utilise des références pour construire des structures de données d'arbre (tableau) au moyen d'un plat (tableau). Il n'y a aucun SPL impliqué parce que je n'ai pas senti qu'il y avait un besoin pour cela. Il y a a gist on GitHub avec un meilleur jeu de couleurs :)

/** 
* Each element in the return array has a 'data' key, holding category data, 
* like name, and a 'children' key holding its subcategories. 
* 
* @param resource $resource MySQL resource resulted from mysql_query 
* @param string $id_key Name of the 'id' field 
* @param string $parent_id_key Name of the 'parent_id' field 
* @param boolean $use_cache Use cached result from previous calls. Defaults to TRUE 
* @return array 
*/ 
function categories($resource, $id_key, $parent_id_key, $use_cache = true) { 
    // Cache the categories in a static local variable. This way, the query 
    // will be executed just for the first function call. Subsequent calls 
    // will return imediatelly, unless you tell it not to. 
    static $tree = array(); 

    if ($tree && $use_cache) { 
     return $tree; 
    } 

    // Flat representation of the categories for fast retrieval using array 
    // keys. Each element will be referenced in the $tree array. This 
    // allows to build a tree data structure using a flat one. 
    $flat = array(); 

    // Reset the $tree, in case $use_cache=false in a subsequent call 
    $tree = array(); 

    while ($row = mysql_fetch_object($resource)) { 
     $flat[$row->$id_key] = array(
      'data' => $row, 
      'children' => array(), 
     ); 

     if (array_key_exists($row->$parent_id_key, $flat)) { 
      // Assign children by reference so that possible subcategories of 
      // this one will appear in the tree structure ($tree) 
      $flat[$row->$parent_id_key]['children'][] =& $flat[$row->$id_key]; 
     } 

     if ($row->$parent_id_key == 0) { 
      // Assign by reference for synchronizing $flat with $tree; 
      $tree[] =& $flat[$row->$id_key]; 
     } 
    } 

    return $tree; 
} 

En outre, la fonction est découplée de la structure de la base de données. Vous devez lui passer une ressource mysql_query, une chaîne représentant le champ id et une chaîne représentant le champ parent_id. La mauvaise partie est qu'il est couplé à l'extension PHP mysql car il utilise un appel à mysql_fetch_object. Cela pourrait probablement être amélioré.

Un autre avantage est qu'il met en cache le résultat pour les appels conséquents, sauf si vous lui dites d'invalider le cache, qui est le quatrième paramètre (booléen).

Jetez un coup d'oeil et voyez si cela vous aide.

+0

"... la fonction est totalement découplée de la base de données, vous devez lui passer une requête mysql_query ..." contradictoire. –

+0

@LeviMorrison merci. Je voulais dire la structure de la base de données, c'est-à-dire qu'elle ne fait aucune supposition sur le nom des champs. Dans peut probablement généralisé encore plus loin, pour travailler avec d'autres fournisseurs de DB. Quoi qu'il en soit, maintenant je suis un peu plus vieux, et je n'utiliserais probablement plus cette fonction :) –

3

Si vos données sont strictement hiérarchiques, et que cela semble être le cas, je recommanderais la méthode Modified Preorder Tree Traversal pour stocker vos données. Il y a un superb article at Sitepoint qui discute de ce problème. Vous semblez utiliser le modèle de liste d'adjacence, qui est discuté à la page un, mais le MPTT est beaucoup plus efficace pour un stockage à haute résolution de ce type de données.

Consultez page 2 pour voir les exemples. C'est vraiment un excellent morceau d'architecture.

+0

Je crois que les raisons pour lesquelles le modèle de liste d'adjacence est déconseillé ont été atténuées par ma solution. Dans ma fonction il y a juste une requête et pas de récursivité, juste une itération. –

+0

Si votre arbre est petit, je pense que votre solution est correcte. Cependant, si vous imaginez un arbre avec 1 000 ou même un million de nœuds, vous ne voudriez pas lire toute la table en mémoire.MPTT a l'avantage que n'importe quel segment de l'arbre peut être isolé dans une seule requête, qu'il s'agisse de l'arbre entier ou d'un chemin vers un seul nœud, et il suffit de lire les enregistrements qui font partie du chemin, rien d'autre. Certainement un peu plus de travail à mettre en œuvre cependant. – zombat