webentwicklung-frage-antwort-db.com.de

PHP Baumstruktur für Kategorien und Unterkategorien, ohne eine Abfrage zu durchlaufen

Ich versuche, eine Liste von Kategorien mit einer beliebigen Anzahl von Unterkategorien zu erstellen, wobei Unterkategorien auch eigene Unterkategorien haben können.

Ich habe alle Kategorien aus der Mysql-Datenbank ausgewählt. Die Katzen befinden sich in einer Standard-Array-Liste für assoziierte Arrays. Jede Kategorie hat eine ID, einen Namen und eine Eltern-ID, wobei die Eltern-ID 0 ist, wenn sie die oberste Ebene ist.

Im Grunde möchte ich in der Lage sein, das einstufige Array von Katzen in eine mehrdimensionale Arraystruktur zu verwandeln, in der jede Kategorie ein Element enthalten kann, das ein Array von Unterkatzen enthält.

Nun, ich kann dies leicht erreichen, indem ich für jede Kategorie eine Abfrage durchläuft. Dies ist jedoch alles andere als ideal. Ich versuche, dies ohne zusätzliche Zugriffe auf die Datenbank zu tun.

Ich verstehe, dass ich dafür eine rekursive Funktion brauche. Kann mir jemand die richtige Richtung für diese Baumstruktur geben?

Prost

34
John

Das erledigt die Arbeit:

$items = array(
        (object) array('id' => 42, 'parent_id' => 1),
        (object) array('id' => 43, 'parent_id' => 42),
        (object) array('id' => 1,  'parent_id' => 0),
);

$childs = array();

foreach($items as $item)
    $childs[$item->parent_id][] = $item;

foreach($items as $item) if (isset($childs[$item->id]))
    $item->childs = $childs[$item->id];

$tree = $childs[0];

print_r($tree);

Dies funktioniert, indem Kategorien zuerst mit parent_id indiziert werden. Dann müssen wir für jede Kategorie nur category->childs auf childs[category->id] setzen, und der Baum wird erstellt!

Nun ist $tree der Kategorienbaum. Es enthält ein Array von Elementen mit parent_id = 0, die selbst ein Array ihrer untergeordneten Elemente enthalten, die selbst ...

Ausgabe von print_r($tree):

stdClass Object
(
    [id] => 1
    [parent_id] => 0
    [childs] => Array
        (
            [0] => stdClass Object
                (
                    [id] => 42
                    [parent_id] => 1
                    [childs] => Array
                        (
                            [0] => stdClass Object
                                (
                                    [id] => 43
                                    [parent_id] => 42
                                )

                        )

                )

        )

)

Hier ist also die letzte Funktion:

function buildTree($items) {

    $childs = array();

    foreach($items as $item)
        $childs[$item->parent_id][] = $item;

    foreach($items as $item) if (isset($childs[$item->id]))
        $item->childs = $childs[$item->id];

    return $childs[0];
}

$tree = buildTree($items);


Hier ist die gleiche Version mit Arrays, was ein bisschen schwierig ist, da wir mit Referenzen spielen müssen (funktioniert aber genauso gut):

$items = array(
        array('id' => 42, 'parent_id' => 1),
        array('id' => 43, 'parent_id' => 42),
        array('id' => 1,  'parent_id' => 0),
);

$childs = array();
foreach($items as &$item) $childs[$item['parent_id']][] = &$item;
unset($item);

foreach($items as &$item) if (isset($childs[$item['id']]))
        $item['childs'] = $childs[$item['id']];
unset($item);

$tree = $childs[0];

Also die Array-Version der finalen Funktion:

function buildTree($items) {

    $childs = array();

    foreach($items as &$item) $childs[$item['parent_id']][] = &$item;
    unset($item);

    foreach($items as &$item) if (isset($childs[$item['id']]))
            $item['childs'] = $childs[$item['id']];

    return $childs[0];
}

$tree = buildTree($items);
78
Arnaud Le Blanc

Sie können alle Kategorien gleichzeitig abrufen.

Angenommen, Sie haben ein unvollständiges Ergebnis aus der Datenbank:

$categories = array(
    array('id' => 1,  'parent' => 0, 'name' => 'Category A'),
    array('id' => 2,  'parent' => 0, 'name' => 'Category B'),
    array('id' => 3,  'parent' => 0, 'name' => 'Category C'),
    array('id' => 4,  'parent' => 0, 'name' => 'Category D'),
    array('id' => 5,  'parent' => 0, 'name' => 'Category E'),
    array('id' => 6,  'parent' => 2, 'name' => 'Subcategory F'),
    array('id' => 7,  'parent' => 2, 'name' => 'Subcategory G'),
    array('id' => 8,  'parent' => 3, 'name' => 'Subcategory H'),
    array('id' => 9,  'parent' => 4, 'name' => 'Subcategory I'),
    array('id' => 10, 'parent' => 9, 'name' => 'Subcategory J'),
);

Sie können eine einfache Funktion erstellen, die diese flache Liste in eine Struktur verwandelt, vorzugsweise innerhalb einer Funktion. Ich verwende Pass-by-Reference, damit es nur ein Array pro Kategorie gibt und nicht mehrere Kopien des Arrays für eine Kategorie.

function categoriesToTree(&$categories) {

Eine Karte wird zum schnellen Nachschlagen von Kategorien verwendet. Hier habe ich auch ein Dummy-Array für die "root" -Ebene erstellt.

    $map = array(
        0 => array('subcategories' => array())
    );

Ich habe jedem Kategorien-Array ein weiteres Feld, Unterkategorien, hinzugefügt und es der Karte hinzugefügt.

    foreach ($categories as &$category) {
        $category['subcategories'] = array();
        $map[$category['id']] = &$category;
    }

Erneutes Durchlaufen aller Kategorien und Hinzufügen zur Unterkategorieliste des übergeordneten Objekts. Die Referenz ist hier wichtig, andernfalls werden die bereits hinzugefügten Kategorien nicht aktualisiert, wenn weitere Unterkategorien vorhanden sind.

    foreach ($categories as &$category) {
        $map[$category['parent']]['subcategories'][] = &$category;
    }

Geben Sie schließlich die Unterkategorien dieser Dummy-Kategorie zurück, die sich auf alle Kategorien der obersten Ebene beziehen._

    return $map[0]['subcategories'];

}

Verwendungszweck:

$tree = categoriesToTree($categories);

Und hier ist der Code in Aktion auf Codepad .

16
Thai

Siehe die Methode:

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['id']] = $element;
        }
    }
    return $branch;
}
1
user6199980

Ich hatte das gleiche Problem und löste es auf diese Weise: Katzenreihen von DB abrufen und für jede Wurzelkategorie Baum erstellen, beginnend mit Stufe (Tiefe) 0. Möglicherweise nicht die effizienteste Lösung, aber für mich.

$globalTree = array();
$fp = fopen("/tmp/taxonomy.csv", "w");

// I get categories from command line, but if you want all, you can fetch from table
$categories = $db->fetchCol("SELECT id FROM categories WHERE parentid = '0'");

foreach ($categories as $category) {
    buildTree($category, 0);
    printTree($category);
    $globalTree = array();
}

fclose($file);

function buildTree($categoryId, $level)
{
    global $db, $globalTree;
    $rootNode = $db->fetchRow("SELECT id, name FROM categories WHERE id=?", $categoryId);
    $childNodes = $db->fetchAll("SELECT * FROM categories WHERE parentid = ? AND id <> ? ORDER BY id", array($rootNode['id'], $rootNode['id']));
    if(count($childNodes) < 1) {
        return 0;
    } else {
        $childLvl = $level + 1;
        foreach ($childNodes as $childNode) {
            $id = $childNode['id'];
            $childLevel = isset($globalTree[$id])? max($globalTree[$id]['depth'], $level): $level;
            $globalTree[$id] = array_merge($childNode, array('depth' => $childLevel));
            buildTree($id, $childLvl);
        }
    }
}

function printTree($categoryId) {
    global $globalTree, $fp, $db;
    $rootNode = $db->fetchRow("SELECT id, name FROM categories WHERE id=?", $categoryId);
    fwrite($fp, $rootNode['id'] . " : " . $rootNode['name'] . "\n");
    foreach ($globalTree as $node) {
        for ($i=0; $i <= $node['depth']; $i++) {
            fwrite($fp, ",");
        }
        fwrite($fp, $node['id'] " : " . $node['name'] . "\n");
    }
}

ps. Ich bin mir bewusst, dass OP nach einer Lösung ohne DB-Abfragen sucht, aber diese beinhaltet Rekursion und wird jedem helfen, der über diese Frage gestolpert ist und nach rekursiven Lösungen für diese Art von Frage sucht.

0
dhavald