Rekursive Funktion zum Generieren eines mehrdimensionalen Arrays aus dem Datenbankergebnis

81

Ich möchte eine Funktion schreiben, die ein Array von Seiten / Kategorien (aus einem flachen Datenbankergebnis) verwendet und ein Array von verschachtelten Seiten- / Kategorieelementen basierend auf den übergeordneten IDs generiert. Ich würde dies gerne rekursiv machen, damit jede Ebene der Verschachtelung durchgeführt werden kann.

Beispiel: Ich rufe alle Seiten in einer Abfrage ab, und so sieht die Datenbanktabelle aus

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

Und dies ist das Array, mit dem ich am Ende in meinen Ansichtsdateien arbeiten möchte:

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
        )
)

Ich habe fast jede Lösung gesucht und ausprobiert, auf die ich gestoßen bin (es gibt viele hier auf Stack Overflow, aber ich hatte kein Glück, etwas Generisches zu bekommen, das sowohl für Seiten als auch für Kategorien funktioniert.

Hier ist der nächste, den ich bekommen habe, aber es funktioniert nicht, weil ich die Kinder dem Elternteil der ersten Ebene zuordne.

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);
}
David Hemphill
quelle
1
Können Sie nicht einfach mit einem Array aller parent_ids und einem anderen Array für Ihre Seiten arbeiten?
Djot

Antworten:

232

Einige sehr einfache, generische Baumbauten:

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);

Der Algorithmus ist ziemlich einfach:

  1. Nehmen Sie das Array aller Elemente und die ID des aktuellen übergeordneten Elements (anfangs 0/ nichts / null/ was auch immer).
  2. Durchlaufen Sie alle Elemente.
  3. Wenn das parent_ideines Elements mit der aktuellen übergeordneten ID übereinstimmt, die Sie in 1. erhalten haben, ist das Element ein untergeordnetes Element des übergeordneten Elements. Tragen Sie es in Ihre Liste der aktuellen Kinder ein (hier :) $branch.
  4. Rufen Sie die Funktion rekursiv mit der ID des Elements auf, das Sie gerade in 3. identifiziert haben, dh suchen Sie alle untergeordneten Elemente dieses Elements und fügen Sie sie als childrenElement hinzu.
  5. Geben Sie Ihre Liste der gefundenen Kinder zurück.

Mit anderen Worten, eine Ausführung dieser Funktion gibt eine Liste von Elementen zurück, die untergeordnete Elemente der angegebenen übergeordneten ID sind. Rufen Sie es mit auf buildTree($myArray, 1), es wird eine Liste von Elementen zurückgegeben, die die übergeordnete ID 1 haben. Anfangs wird diese Funktion aufgerufen, wobei die übergeordnete ID 0 ist, sodass Elemente ohne übergeordnete ID zurückgegeben werden, die Stammknoten sind. Die Funktion ruft sich rekursiv auf, um Kinder von Kindern zu finden.

täuschen
quelle
1
Ich bin froh, dass es hilft. Zu beachten: Dies ist etwas ineffizient, da immer das gesamte $elementsArray weitergegeben wird. Bei kleinen Arrays spielt das kaum eine Rolle, bei großen Datenmengen sollten Sie jedoch das bereits übereinstimmende Element entfernen, bevor Sie es weitergeben. Das wird allerdings etwas chaotisch, deshalb habe ich es zum leichteren Verständnis einfach gelassen. :)
täuschen
6
@deceze Ich würde gerne auch die chaotische Version sehen. Danke im Voraus!
Jens Törnell
Kann jemand erklären, was das erste Argument 'Array' in buildTree () ist? Sollte das die Variable sein, die Sie dem anfänglichen Array von Seiten usw. geben, z. B. '$ tree = array'? Kann jemand auch die letzte Zeile '$ tree = buildTree ($ rows)' erklären, da '$ rows' nirgendwo definiert ist? Zuletzt habe ich Probleme mit dem HTML-Markup, um die verschachtelte Liste zu generieren.
Brownrice
1
@user arrayist eine Art Hinweis für $elementsdas erste Argument ist einfach array $elements. $rowsist ein Array von Datenbankergebnissen wie das aus der Frage.
Täuschung
1
@user Ich habe eine Erklärung hinzugefügt. Ignorieren Sie das $children = buildTree(...)Teil und die Funktion sollte ziemlich offensichtlich und einfach sein.
Täuschung
13

Ich weiß, dass diese Frage alt ist, aber ich hatte ein sehr ähnliches Problem - außer mit einer sehr großen Datenmenge. Nach einigem Kampf gelang es mir, den Baum in einem Durchgang der Ergebnismenge zu erstellen - unter Verwendung von Referenzen. Dieser Code ist nicht schön, aber er funktioniert und es funktioniert ziemlich schnell. Es ist nicht rekursiv - das heißt, es gibt nur einen Durchgang über die Ergebnismenge und dann einen array_filteram Ende:

$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);

Bei Ausführung mit diesen Daten:

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 |
+------+-------------+

Der letzte print_rerzeugt diese Ausgabe:

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
                                (
                                )

                        )

                )

        )

)

Welches ist genau das, wonach ich gesucht habe.

Aleks G.
quelle
Obwohl die Lösung klug ist, aber dieser Code einen Fehler aufweist, hat er mir in verschiedenen Situationen unterschiedliche Ergebnisse gebracht
Mohammadhzp
@Mohammadhzp Ich habe diese Lösung im letzten Jahr in der Produktion verwendet und hatte kein Problem damit. Wenn Ihre Daten unterschiedlich sind, erhalten Sie unterschiedliche Ergebnisse :)
Aleks G
@AleksG: Ich habe Ihre Antwort vor dem Kommentar positiv bewertet. Ich musste isset ($ elem ['children']) zum Array_filter-Rückruf hinzufügen, so etwas wie dieses return isset ($ elem ['children']) && is_null ($ elem [' n_parent_id ']); damit es richtig funktioniert
Mohammadhzp
@Mohammadhzp Mit Ihrer Änderung funktioniert der Code nicht, wenn ein Element der obersten Ebene keine untergeordneten Elemente hat - es wird vollständig aus dem Array entfernt.
Aleks G
@AleksG, mit der Verwendung der neuesten Version von PHP ist das nicht das, was für mich passiert. Wenn ich isset ($ elem ['children']) entferne und das Element der obersten Ebene untergeordnete Elemente entferne, würde ich zwei verschiedene Arrays dieser obersten Ebene erhalten Element (eins mit Kindern und eins ohne) "Ich habe gerade vor 2 Minuten erneut getestet und ohne isset () bekomme ich ein anderes (falsches) Ergebnis"
Mohammadhzp
-1

Es ist möglich, PHP zu verwenden, um das MySQL-Ergebnis in ein Array zu bekommen und es dann zu verwenden.

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