Question

Je crée une arborescence de catégories avec des parentid qui peuvent être appelées par des enfants de la manière suivante:

ID | Name | ParentID
1    1      0
2    2      1
3    3      2
4    4      1

Résultat:

1 = 1
2 = 1 -> 2
3 = 1 -> 2 -> 3
4 = 1 -> 4

ce qui signifie que 3 est un enfant de 2 , qui est un enfant de 1

.

en essayant d’obtenir cette idée (avec le - > pour montrer quelles relations sont définies), je n’arrive qu’à la deuxième année (1 - & 2) mais pas à la troisième (1- > 2- > 3) à cause de la fonction de bouclage que j'utilise pour cela.

//put all ID's in an array
while ($row2 = $connector->fetchArray($result2)){
 $id = $row2['ID'];
 $parents[$id] = $row2['name'];
}

// show the tree-structure
while ($row = $connector->fetchArray($result)){
    if($row['parentid']!=0)echo $parents[$row['parentid']].' -> ';
    echo $row['name'].' -    ';
    echo '<br>';
}

J'aimerais que deux choses changent:

  
      
  1. demandez au code de générer automatiquement une arborescence de la taille voulue.
  2.   
  3. dans les boucles while, je dois sélectionner le résultat $ deux fois (une fois en tant que $ result, une fois en tant que $ result2) pour le faire fonctionner. Ces $ résultats ont exactement la même requête de base de données:
    SELECT ID, nom, parentid FROM catégories
  4.   

pour récupérer les résultats. J'aimerais déclarer cela une seule fois.



Merci pour toutes les bonnes réponses. J'ai opté pour l'approche la plus simple, moins codée à implémenter:

$result = $connector->query('SELECT ID,name,parentid FROM categories');

// Get an array containing the results.
$parents = array();
while ($row = $connector->fetchArray($result)){
  $id = $row['ID'];
  $parents[$id] = array('ID' => $row['ID'],'name' => $row['name'],'parentid' => $row['parentid']);
}

foreach ($parents as $id => $row){
  $pid=$id;
  $arrTmp= array();
  do {      // iterate through all parents until top is reached
    $arrTmp[]=$pid;
    $pid = $parents[$pid]['parentid'];
  }while ($pid != 0);
    $arrTmp = array_reverse($arrTmp);
  foreach($arrTmp as $id){
    echo $parents[$id]['name'].' -&gt; ';
    }
  echo '<br>';
}
Était-ce utile?

La solution

Si vous voulez vraiment créer des hiérarchies avec des identifiants parents (convient uniquement pour un petit nombre d'éléments / hiérarchies)

J'ai légèrement modifié votre code (je ne l'ai pas testé, il peut donc y avoir des erreurs de syntaxe):

//put all recordsets in an array to save second query
while ($row2 = $connector->fetchArray($result2)){
  $id = $row2['ID'];
  $parents[$id] = array('name' => $row2['name'],'parent' => $row2['parentid']);
}

// show the tree-structure
foreach ($parents as $id => $row){
  $pid = $row['parentid'];
  while ($pid != 0){      // iterate through all parents until top is reached
    echo $parents[$pid]['name'].' -&gt; ';
    $pid = $parents[$pid]['parentid'];
  }
  echo $parents[$id]['name'].' -    ';
  echo '<br>';
}

Pour répondre à votre commentaire:

$parents = array();
$parents[2] = array('ID'=>2,'name'=>'General','parentid'=>0); 
$parents[3] = array('ID'=>3,'name'=>'Gadgets','parentid'=>2); 
$parents[4] = array('ID'=>4,'name'=>'iPhone','parentid'=>3); 

foreach ($parents as $id => $row){
  $pid=$id;
  $arrTmp= array();
  do {      // iterate through all parents until top is reached
    $arrTmp[]=$pid;
    $pid = $parents[$pid]['parentid'];
  }while ($pid != 0);
    $arrTmp = array_reverse($arrTmp);
  foreach($arrTmp as $id){
    echo $parents[$id]['name'].' -&gt; ';
    }
  echo '<br>';
}

Imprime:

  

Général - >

     

Général - > Gadgets - >

     

Général - > Gadgets - > iPhone - >

Autres conseils

Plutôt que de faire en sorte que PHP organise les éléments dans un arbre, pourquoi ne pas demander à la base de données de le faire pour vous? J'ai trouvé cet article sur les données hiérarchiques très bien et la les exemples sont presque identiques aux vôtres.

MODIFIER

Le code SQL permettant d'obtenir l'arborescence complète à l'aide du modèle d'adjacence n'est pas idéal. Comme l'explique l'article, cela nécessite plutôt beaucoup de jointures, même pour une petite hiérarchie. Ne pouvez-vous pas utiliser l'approche Ensemble imbriqué? Le code SQL reste le même quelle que soit la taille de la hiérarchie et INSERT et DELETE ne devraient pas être très difficiles non plus.

Peut-être plus facile avec la POO. Il suffit de trier la requête par parentId

Remarque: la méthode listChildren et l’impression au bas de la page montrent simplement qu’elle est répertoriée correctement. Je n'ai pas interprété la question selon laquelle l'affichage était important.

class Element {
    public $id;
    public $name;
    public $parent = null;
    public $children = array();

    public function __construct($id, $name)
    {
        $this->id = $id;
        $this->name = $name;
    }

    public function addChild($element)
    {
        $this->children[$element->id] = $element;
        $element->setParent($this);
    }

    public function setParent($element)
    {
        $this->parent = $element;
    }

    public function hasChildren()
    {
        return !empty($this->children);
    }

    public function listChildren()
    {
        if (empty($this->children)) {
            return null;
        }

        $out = array();
        foreach ($this->children as $child) {
            $data = $child->id . ':' . $child->name;
            $subChildren = $child->listChildren();
            if ($subChildren !== null) {
                $data .= '[' . $subChildren . ']';
            }
            $out[] = $data;
        }
        return implode(',', $out);
    }
}

$elements = array();
$noParents = array();
while ($row = $connector->fetchArray($result)) {
    $elements[$row['id']] = $element = new Element($row['id'], $row['name']);

    if (isset($elements[$row['parent']])) {
        $elements[$row['parent']]->addChild($element);
    } else {
        $noParents[] = $element;
    }
}

foreach ($noParents as $element) {
    if ($element->hasChildren()) {
        echo "Element {$element->id} has children {$element->listChildren()}.\n";
    } else {
        echo "Element {$element->id} has no children.\n";
    }
}

Si vous utilisez PostgreSQL comme base de données, vous pouvez utiliser le connectby () pour créer le jeu d'enregistrements:

SELECT * 
FROM connectby('tableName', 'id', 'parent_id') 
    AS t(keyid text, parent_keyid text, level int);

J'aime cette fonction et l'utilise tout le temps dans mon code. Il peut faire des choses très puissantes, très rapidement, et vous ne devez pas maintenir les valeurs gauche / droite comme le modèle ( adjacency ).

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top