web-dev-qa-db-fra.com

Fonction récursive pour générer un tableau multidimensionnel à partir du résultat de la base de données

Je cherche à écrire une fonction qui prend un tableau de pages/catégories (à partir d'un résultat de base de données plat) et génère un tableau d'éléments de page/catégorie imbriqués basés sur les identificateurs parent. Je voudrais faire cela de manière récursive, de sorte que n'importe quel niveau d'imbrication puisse être fait.

Par exemple: je vais chercher toutes les pages dans une requête, et voici à quoi ressemble la table de base de données

+-------+---------------+---------------------------+
|   id  |   parent_id   |           title           |
+-------+---------------+---------------------------+
|   1   |       0       |   Parent Page             |
|   2   |       1       |   Sub Page                |
|   3   |       2       |   Sub Sub Page            |
|   4   |       0       |   Another Parent Page     |
+-------+---------------+---------------------------+

Et voici le tableau avec lequel je voudrais finir de traiter dans mes fichiers de vue:

Array
(
    [0] => Array
        (
            [id] => 1
            [parent_id] => 0
            [title] => Parent Page
            [children] => Array
                        (
                            [0] => Array
                                (
                                    [id] => 2
                                    [parent_id] => 1
                                    [title] => Sub Page
                                    [children] => Array
                                                (
                                                    [0] => Array
                                                        (
                                                            [id] => 3
                                                            [parent_id] => 1
                                                            [title] => Sub Sub Page
                                                        )
                                                )
                                )
                        )
        )
    [1] => Array
        (
            [id] => 4
            [parent_id] => 0
            [title] => Another Parent Page
        )
)

J'ai examiné et essayé presque toutes les solutions que j'ai rencontrées (il y en a beaucoup ici sur Stack Overflow, mais je n'ai pas eu la chance d'obtenir quelque chose de générique qui convienne aux pages et aux catégories.

Voici le plus proche que j'ai eu, mais cela ne fonctionne pas parce que j'assigne les enfants au parent de premier niveau.

function page_walk($array, $parent_id = FALSE)
{   
    $organized_pages = array();

    $children = array();

    foreach($array as $index => $page)
    {
        if ( $page['parent_id'] == 0) // No, just spit it out and you're done
        {
            $organized_pages[$index] = $page;
        }
        else // If it does, 
        {       
            $organized_pages[$parent_id]['children'][$page['id']] = $this->page_walk($page, $parent_id);
        }
    }

    return $organized_pages;
}

function page_list($array)
{       
    $fakepages = array();
    $fakepages[0] = array('id' => 1, 'parent_id' => 0, 'title' => 'Parent Page');
    $fakepages[1] = array('id' => 2, 'parent_id' => 1, 'title' => 'Sub Page');
    $fakepages[2] = array('id' => 3, 'parent_id' => 2, 'title' => 'Sub Sub Page');
    $fakepages[3] = array('id' => 4, 'parent_id' => 3, 'title' => 'Another Parent Page');

    $pages = $this->page_walk($fakepages, 0);

    print_r($pages);
}
74
David Hemphill

Quelques constructions d'arbres génériques très simples:

function buildTree(array $elements, $parentId = 0) {
    $branch = array();

    foreach ($elements as $element) {
        if ($element['parent_id'] == $parentId) {
            $children = buildTree($elements, $element['id']);
            if ($children) {
                $element['children'] = $children;
            }
            $branch[] = $element;
        }
    }

    return $branch;
}

$tree = buildTree($rows);

L'algorithme est assez simple:

  1. Prenez le tableau de tous les éléments et l'id du parent actuel (initialement 0/None/null/peu importe).
  2. Boucle à travers tous les éléments.
  3. Si le parent_id D'un élément correspond à l'ID de parent actuel que vous avez obtenu dans 1., l'élément est un enfant du parent. Mettez-le dans votre liste d'enfants actuels (ici: $branch).
  4. Appelez la fonction de manière récursive avec l’id de l’élément que vous venez d’identifier en 3. Trouvez tous les enfants de cet élément et ajoutez-les en tant qu’élément children.
  5. Renvoyez votre liste d'enfants trouvés.

En d'autres termes, une exécution de cette fonction retourne une liste d'éléments qui sont les enfants de l'identifiant parent donné. Appelez-le avec buildTree($myArray, 1), il renverra une liste d'éléments portant l'identifiant parent 1. Initialement, cette fonction est appelée avec l'identifiant parent égal à 0. Les éléments sans identifiant parent sont donc retournés, qui sont des nœuds racine. La fonction appelle elle-même de manière récursive pour trouver des enfants d'enfants.

204
deceze

Je sais que cette question est ancienne, mais je faisais face à un problème très similaire - sauf avec une très grande quantité de données. Après quelques efforts, j'ai réussi à construire l'arbre en un seul passage du jeu de résultats - en utilisant des références. Ce code n'est pas joli, mais ça marche et ça marche assez vite. C'est non-récursif - en d'autres termes, il n'y a qu'un seul passage sur le jeu de résultats, puis un array_filter à la fin:

$dbh = new PDO(CONNECT_STRING, USERNAME, PASSWORD);
$dbs = $dbh->query("SELECT n_id, n_parent_id from test_table order by n_parent_id, n_id");
$elems = array();

while(($row = $dbs->fetch(PDO::FETCH_ASSOC)) !== FALSE) {
    $row['children'] = array();
    $vn = "row" . $row['n_id'];
    ${$vn} = $row;
    if(!is_null($row['n_parent_id'])) {
        $vp = "parent" . $row['n_parent_id'];
        if(isset($data[$row['n_parent_id']])) {
            ${$vp} = $data[$row['n_parent_id']];
        }
        else {
            ${$vp} = array('n_id' => $row['n_parent_id'], 'n_parent_id' => null, 'children' => array());
            $data[$row['n_parent_id']] = &${$vp};
        }
        ${$vp}['children'][] = &${$vn};
        $data[$row['n_parent_id']] = ${$vp};
    }
    $data[$row['n_id']] = &${$vn};
}
$dbs->closeCursor();

$result = array_filter($data, function($elem) { return is_null($elem['n_parent_id']); });
print_r($result);

Lorsqu'il est exécuté sur ces données:

mysql> select * from test_table;
+------+-------------+
| n_id | n_parent_id |
+------+-------------+
|    1 |        NULL |
|    2 |        NULL |
|    3 |           1 |
|    4 |           1 |
|    5 |           2 |
|    6 |           2 |
|    7 |           5 |
|    8 |           5 |
+------+-------------+

Le dernier print_r produit cette sortie:

Array
(
    [1] => Array
        (
            [n_id] => 1
            [n_parent_id] => 
            [children] => Array
                (
                    [3] => Array
                        (
                            [n_id] => 3
                            [n_parent_id] => 1
                            [children] => Array
                                (
                                )

                        )

                    [4] => Array
                        (
                            [n_id] => 4
                            [n_parent_id] => 1
                            [children] => Array
                                (
                                )

                        )

                )

        )

    [2] => Array
        (
            [n_id] => 2
            [n_parent_id] => 
            [children] => Array
                (
                    [5] => Array
                        (
                            [n_id] => 5
                            [n_parent_id] => 2
                            [children] => Array
                                (
                                    [7] => Array
                                        (
                                            [n_id] => 7
                                            [n_parent_id] => 5
                                            [children] => Array
                                                (
                                                )

                                        )

                                    [8] => Array
                                        (
                                            [n_id] => 8
                                            [n_parent_id] => 5
                                            [children] => Array
                                                (
                                                )

                                        )

                                )

                        )

                    [6] => Array
                        (
                            [n_id] => 6
                            [n_parent_id] => 2
                            [children] => Array
                                (
                                )

                        )

                )

        )

)

C'est exactement ce que je cherchais.

12
Aleks G

Il est possible d'utiliser php pour obtenir le résultat mysql dans un tableau, puis l'utiliser.

$categoryArr = Array();
while($categoryRow = mysql_fetch_array($category_query_result)){
    $categoryArr[] = array('parentid'=>$categoryRow['parent_id'],
            'id'=>$categoryRow['id']);
   }
0
mustafa