it-swarm.com.de

Umwandlung einer Reihe von Eltern-Kind-Beziehungen in einen hierarchischen Baum?

Ich habe eine Reihe von Namen-Eltern-Namen-Paaren, die ich in möglichst wenige hierarchische Baumstrukturen umwandeln möchte. Dies können zum Beispiel die Paarungen sein:

Child : Parent
    H : G
    F : G
    G : D
    E : D
    A : E
    B : C
    C : E
    D : NULL

Welcher muss in (a) einen (oder mehrere) Baums verwandelt werden:

D
├── E
│   ├── A
│   │   └── B
│   └── C   
└── G
    ├── F
    └── H

Das Endergebnis, das ich möchte, ist eine verschachtelte Menge von <ul>-Elementen, wobei jeder <li> den Namen des Kindes enthält.

Es gibt keine Inkonsistenzen in den Paarungen (Kind ist sein eigenes Elternteil, Elternteil ist Kind, usw.), so dass wahrscheinlich eine Reihe von Optimierungen vorgenommen werden kann.

Wie würde ich in PHP von einem Array mit untergeordneten Elternpaaren zu einem Satz verschachtelter <ul>s wechseln?

Ich habe das Gefühl, dass eine Rekursion involviert ist, aber ich bin nicht ganz wach genug, um darüber nachzudenken.

92
Eric

Dies erfordert eine sehr grundlegende rekursive Funktion, um die untergeordneten/übergeordneten Paare in einer Baumstruktur zu parsen, und eine weitere rekursive Funktion, um sie auszudrucken. Nur eine Funktion würde ausreichen, hier sind jedoch zwei zur Verdeutlichung (eine kombinierte Funktion befindet sich am Ende dieser Antwort).

Initialisieren Sie zunächst das Array von Child/Parent-Paaren:

$tree = array(
    'H' => 'G',
    'F' => 'G',
    'G' => 'D',
    'E' => 'D',
    'A' => 'E',
    'B' => 'C',
    'C' => 'E',
    'D' => null
);

Dann die Funktion, die dieses Array in eine hierarchische Baumstruktur parst:

function parseTree($tree, $root = null) {
    $return = array();
    # Traverse the tree and search for direct children of the root
    foreach($tree as $child => $parent) {
        # A direct child is found
        if($parent == $root) {
            # Remove item from tree (we don't need to traverse this again)
            unset($tree[$child]);
            # Append the child into result array and parse its children
            $return[] = array(
                'name' => $child,
                'children' => parseTree($tree, $child)
            );
        }
    }
    return empty($return) ? null : $return;    
}

Und eine Funktion, die diesen Baum durchquert, um eine ungeordnete Liste auszudrucken:

function printTree($tree) {
    if(!is_null($tree) && count($tree) > 0) {
        echo '<ul>';
        foreach($tree as $node) {
            echo '<li>'.$node['name'];
            printTree($node['children']);
            echo '</li>';
        }
        echo '</ul>';
    }
}

Und die tatsächliche Verwendung:

$result = parseTree($tree);
printTree($result);

Hier ist der Inhalt von $result:

Array(
    [0] => Array(
        [name] => D
        [children] => Array(
            [0] => Array(
                [name] => G
                [children] => Array(
                    [0] => Array(
                        [name] => H
                        [children] => NULL
                    )
                    [1] => Array(
                        [name] => F
                        [children] => NULL
                    )
                )
            )
            [1] => Array(
                [name] => E
                [children] => Array(
                    [0] => Array(
                        [name] => A
                        [children] => NULL
                    )
                    [1] => Array(
                        [name] => C
                        [children] => Array(
                            [0] => Array(
                                [name] => B
                                [children] => NULL
                            )
                        )
                    )
                )
            )
        )
    )
)

Wenn Sie ein bisschen mehr Effizienz wünschen, können Sie diese Funktionen zu einer kombinieren und die Anzahl der durchgeführten Iterationen reduzieren:

function parseAndPrintTree($root, $tree) {
    $return = array();
    if(!is_null($tree) && count($tree) > 0) {
        echo '<ul>';
        foreach($tree as $child => $parent) {
            if($parent == $root) {                    
                unset($tree[$child]);
                echo '<li>'.$child;
                parseAndPrintTree($child, $tree);
                echo '</li>';
            }
        }
        echo '</ul>';
    }
}

Sie speichern nur 8 Iterationen für ein so kleines Dataset, aber bei größeren Sets kann dies einen Unterschied machen.

119
Tatu Ulmanen

Noch eine weitere Funktion zum Erstellen eines Baums (keine Rekursion erforderlich, stattdessen Referenzen verwenden):

$array = array('H' => 'G', 'F' => 'G', ..., 'D' => null);

function to_tree($array)
{
    $flat = array();
    $tree = array();

    foreach ($array as $child => $parent) {
        if (!isset($flat[$child])) {
            $flat[$child] = array();
        }
        if (!empty($parent)) {
            $flat[$parent][$child] =& $flat[$child];
        } else {
            $tree[$child] =& $flat[$child];
        }
    }

    return $tree;
}

Gibt ein hierarchisches Array wie dieses zurück:

Array(
    [D] => Array(
        [G] => Array(
            [H] => Array()
            [F] => Array()
        )
        ...
    )
)

Die kann einfach als rekursive HTML-Liste gedruckt werden.

Eine andere, vereinfachte Möglichkeit, die flache Struktur in $tree in eine Hierarchie umzuwandeln. Es ist nur ein temporäres Array erforderlich, um es verfügbar zu machen:

// add children to parents
$flat = array(); # temporary array
foreach ($tree as $name => $parent)
{
    $flat[$name]['name'] = $name; # self
    if (NULL === $parent)
    {
        # no parent, is root element, assign it to $tree
        $tree = &$flat[$name]; 
    }
    else
    {
        # has parent, add self as child    
        $flat[$parent]['children'][] = &$flat[$name];
    }
}
unset($flat);

Das ist alles, um die Hierarchie in ein mehrdimensionales Array zu bringen:

Array
(
    [children] => Array
        (
            [0] => Array
                (
                    [children] => Array
                        (
                            [0] => Array
                                (
                                    [name] => H
                                )

                            [1] => Array
                                (
                                    [name] => F
                                )

                        )

                    [name] => G
                )

            [1] => Array
                (
                    [name] => E
                    [children] => Array
                        (
                            [0] => Array
                                (
                                    [name] => A
                                )

                            [1] => Array
                                (
                                    [children] => Array
                                        (
                                            [0] => Array
                                                (
                                                    [name] => B
                                                )

                                        )

                                    [name] => C
                                )

                        )

                )

        )

    [name] => D
)

Die Ausgabe ist weniger trivial, wenn Sie Rekursion vermeiden möchten (dies kann bei großen Strukturen eine Belastung sein).

Ich wollte immer das UL/LI- "Dilemma" für die Ausgabe eines Arrays lösen. Das Dilemma ist, dass nicht jeder Artikel weiß, ob Kinder nachgehen oder wie viele vorangehende Elemente geschlossen werden müssen. In einer anderen Antwort habe ich das bereits gelöst, indem ich RecursiveIteratorIterator verwendet habe und nach getDepth() und anderen Metainformationen gesucht habe, die von mir selbst Iterator geschrieben wurden: verschachteltes Set-Modell in einen <ul>, aber „geschlossene“ Unterbäume verbergen . Das answer zeigt auch, dass Sie mit Iteratoren sehr flexibel sind.

Dies war jedoch eine vorsortierte Liste, wäre also für Ihr Beispiel nicht geeignet. Außerdem wollte ich dies immer für eine Art Standardbaumstruktur und die HTML-Elemente <ul> und <li> lösen.

Das Grundkonzept, das ich mir ausgedacht habe, ist folgendes:

  1. TreeNode - Bringt jedes Element in einen einfachen TreeNode-Typ zusammen, der seinen Wert (z. B. Name) angeben kann und ob es untergeordnete Elemente enthält.
  2. TreeNodesIterator - Ein RecursiveIterator, das eine Menge (Array) dieser TreeNodes-Elemente durchlaufen kann. Das ist ziemlich einfach, da der TreeNode-Typ bereits weiß, ob er Kinder hat und welche.
  3. RecursiveListIterator - Ein RecursiveIteratorIterator, das alle Ereignisse enthält, die erforderlich sind, wenn rekursiv jede Art von RecursiveIterator: .__ durchlaufen wird.
    • beginIterationendIteration - BEGINN UND ENDE DER HAUPTLISTE.
    • beginElement/endElement - ANFANG UND ENDE JEDES ELEMENTS.
    • beginChildren/endChildren - BEGINN UND ENDE JEDER UNTERGEORDNETEN LISTE . DIESE RecursiveListIterator STELLT DIESE EREIGNISSE NUR IN FORM VON FUNKTIONSAUFRUFEN ZUR VERF&UUML;GUNG. KINDERLISTEN, WIE ES F&UUML;R <ul><li>-LISTEN TYPISCH IST, WERDEN IM &UUML;BERGEORDNETEN <li>-ELEMENT GE&OUML;FFNET UND GESCHLOSSEN. DAHER WIRD DAS endElement-EREIGNIS NACH DEM ENTSPRECHENDEN endChildren-EREIGNIS AUSGEL&OUML;ST. DIES K&OUML;NNTE GE&AUML;NDERT ODER KONFIGURIERBAR GEMACHT WERDEN, UM DIE VERWENDUNG DIESER KLASSE ZU ERWEITERN. DIE EREIGNISSE WERDEN DANN ALS FUNKTIONSAUFRUFE AN EIN DEKORATIONSOBJEKT VERTEILT, UM DIE DINGE VONEINANDER ZU TRENNEN.
  4. _/ListDecorator - Eine "Dekoratorklasse", die nur die Ereignisse von RecursiveListIterator empfängt.

Ich beginne mit der Hauptausgangslogik. Mit dem nun hierarchischen $tree-Array sieht der endgültige Code folgendermaßen aus:

$root = new TreeNode($tree);
$it = new TreeNodesIterator(array($root));
$rit = new RecursiveListIterator($it);
$decor = new ListDecorator($rit);
$rit->addDecorator($decor);

foreach($rit as $item)
{
    $inset = $decor->inset(1);
    printf("%s%s\n", $inset, $item->getName());
}

Schauen wir uns zuerst das ListDecorator an, das einfach die <ul>- und <li>-Elemente umschließt und über die Ausgabe der Listenstruktur entscheidet:

class ListDecorator
{
    private $iterator;
    public function __construct(RecursiveListIterator $iterator)
    {
        $this->iterator = $iterator;
    }
    public function inset($add = 0)
    {
        return str_repeat('  ', $this->iterator->getDepth()*2+$add);
    }

Der Konstruktor übernimmt den Listen-Iterator, an dem er arbeitet. inset ist nur eine Hilfsfunktion für die Nizza-Einrückung der Ausgabe. Der Rest sind nur die Ausgabefunktionen für jedes Ereignis:

    public function beginElement()
    {
        printf("%s<li>\n", $this->inset());
    }
    public function endElement()
    {
        printf("%s</li>\n", $this->inset());
    }
    public function beginChildren()
    {
        printf("%s<ul>\n", $this->inset(-1));
    }
    public function endChildren()
    {
        printf("%s</ul>\n", $this->inset(-1));
    }
    public function beginIteration()
    {
        printf("%s<ul>\n", $this->inset());
    }
    public function endIteration()
    {
        printf("%s</ul>\n", $this->inset());
    }
}

Im Hinblick auf diese Ausgabefunktionen ist dies wieder die Hauptausgabe-Überarbeitung/-schleife. Ich gehe sie Schritt für Schritt durch:

$root = new TreeNode($tree);

Erstellen Sie das Stammverzeichnis TreeNode, das zum Starten der Iteration verwendet wird bei:

$it = new TreeNodesIterator(array($root));

Diese TreeNodesIterator ist eine RecursiveIterator, die eine rekursive Iteration über den einzelnen $root-Knoten ermöglicht. Es wird als Array übergeben, da diese Klasse etwas zum Durchlaufen benötigt und die Wiederverwendung mit einer Gruppe von untergeordneten Elementen ermöglicht, die ebenfalls ein Array von TreeNode-Elementen ist.

$rit = new RecursiveListIterator($it);

Dieses RecursiveListIterator ist ein RecursiveIteratorIterator, das die besagten Ereignisse bereitstellt. Um davon Gebrauch zu machen, muss nur ein ListDecorator bereitgestellt werden (die Klasse oben) und mit addDecorator zugewiesen werden:

$decor = new ListDecorator($rit);
$rit->addDecorator($decor);

Dann ist alles so eingerichtet, dass nur foreach darüber steht und jeder Knoten ausgegeben wird:

foreach($rit as $item)
{
    $inset = $decor->inset(1);
    printf("%s%s\n", $inset, $item->getName());
}

Wie dieses Beispiel zeigt, ist die gesamte Ausgangslogik in der ListDecorator-Klasse und diesem einzigen foreach eingeschlossen. Der gesamte rekursive Durchlauf wurde vollständig in rekursive SPL-Iteratoren eingeschlossen, die eine gestapelte Prozedur bereitstellten. Das bedeutet, dass intern keine Aufrufe von Rekursionsfunktionen durchgeführt werden.

Die ereignisbasierte ListDecorator-Funktion ermöglicht Ihnen, die Ausgabe spezifisch zu ändern und mehrere Arten von Listen für dieselbe Datenstruktur bereitzustellen. Es ist sogar möglich, die Eingabe zu ändern, da die Array-Daten in TreeNode eingekapselt wurden.

Das vollständige Codebeispiel:

<?php
namespace My;

$tree = array('H' => 'G', 'F' => 'G', 'G' => 'D', 'E' => 'D', 'A' => 'E', 'B' => 'C', 'C' => 'E', 'D' => null);

// add children to parents
$flat = array(); # temporary array
foreach ($tree as $name => $parent)
{
    $flat[$name]['name'] = $name; # self
    if (NULL === $parent)
    {
        # no parent, is root element, assign it to $tree
        $tree = &$flat[$name];
    }
    else
    {
        # has parent, add self as child    
        $flat[$parent]['children'][] = &$flat[$name];
    }
}
unset($flat);

class TreeNode
{
    protected $data;
    public function __construct(array $element)
    {
        if (!isset($element['name']))
            throw new InvalidArgumentException('Element has no name.');

        if (isset($element['children']) && !is_array($element['children']))
            throw new InvalidArgumentException('Element has invalid children.');

        $this->data = $element;
    }
    public function getName()
    {
         return $this->data['name'];
    }
    public function hasChildren()
    {
        return isset($this->data['children']) && count($this->data['children']);
    }
    /**
     * @return array of child TreeNode elements 
     */
    public function getChildren()
    {        
        $children = $this->hasChildren() ? $this->data['children'] : array();
        $class = get_called_class();
        foreach($children as &$element)
        {
            $element = new $class($element);
        }
        unset($element);        
        return $children;
    }
}

class TreeNodesIterator implements \RecursiveIterator
{
    private $nodes;
    public function __construct(array $nodes)
    {
        $this->nodes = new \ArrayIterator($nodes);
    }
    public function  getInnerIterator()
    {
        return $this->nodes;
    }
    public function getChildren()
    {
        return new TreeNodesIterator($this->nodes->current()->getChildren());
    }
    public function hasChildren()
    {
        return $this->nodes->current()->hasChildren();
    }
    public function rewind()
    {
        $this->nodes->rewind();
    }
    public function valid()
    {
        return $this->nodes->valid();
    }   
    public function current()
    {
        return $this->nodes->current();
    }
    public function key()
    {
        return $this->nodes->key();
    }
    public function next()
    {
        return $this->nodes->next();
    }
}

class RecursiveListIterator extends \RecursiveIteratorIterator
{
    private $elements;
    /**
     * @var ListDecorator
     */
    private $decorator;
    public function addDecorator(ListDecorator $decorator)
    {
        $this->decorator = $decorator;
    }
    public function __construct($iterator, $mode = \RecursiveIteratorIterator::SELF_FIRST, $flags = 0)
    {
        parent::__construct($iterator, $mode, $flags);
    }
    private function event($name)
    {
        // event debug code: printf("--- %'.-20s --- (Depth: %d, Element: %d)\n", $name, $this->getDepth(), @$this->elements[$this->getDepth()]);
        $callback = array($this->decorator, $name);
        is_callable($callback) && call_user_func($callback);
    }
    public function beginElement()
    {
        $this->event('beginElement');
    }
    public function beginChildren()
    {
        $this->event('beginChildren');
    }
    public function endChildren()
    {
        $this->testEndElement();
        $this->event('endChildren');
    }
    private function testEndElement($depthOffset = 0)
    {
        $depth = $this->getDepth() + $depthOffset;      
        isset($this->elements[$depth]) || $this->elements[$depth] = 0;
        $this->elements[$depth] && $this->event('endElement');

    }
    public function nextElement()
    {
        $this->testEndElement();
        $this->event('{nextElement}');
        $this->event('beginElement');       
        $this->elements[$this->getDepth()] = 1;
    } 
    public function beginIteration()
    {
        $this->event('beginIteration');
    }
    public function endIteration()
    {
        $this->testEndElement();
        $this->event('endIteration');       
    }
}

class ListDecorator
{
    private $iterator;
    public function __construct(RecursiveListIterator $iterator)
    {
        $this->iterator = $iterator;
    }
    public function inset($add = 0)
    {
        return str_repeat('  ', $this->iterator->getDepth()*2+$add);
    }
    public function beginElement()
    {
        printf("%s<li>\n", $this->inset(1));
    }
    public function endElement()
    {
        printf("%s</li>\n", $this->inset(1));
    }
    public function beginChildren()
    {
        printf("%s<ul>\n", $this->inset());
    }
    public function endChildren()
    {
        printf("%s</ul>\n", $this->inset());
    }
    public function beginIteration()
    {
        printf("%s<ul>\n", $this->inset());
    }
    public function endIteration()
    {
        printf("%s</ul>\n", $this->inset());
    }
}


$root = new TreeNode($tree);
$it = new TreeNodesIterator(array($root));
$rit = new RecursiveListIterator($it);
$decor = new ListDecorator($rit);
$rit->addDecorator($decor);

foreach($rit as $item)
{
    $inset = $decor->inset(2);
    printf("%s%s\n", $inset, $item->getName());
}

Ausbruch:

<ul>
  <li>
    D
    <ul>
      <li>
        G
        <ul>
          <li>
            H
          </li>
          <li>
            F
          </li>
        </ul>
      </li>
      <li>
        E
        <ul>
          </li>
          <li>
            A
          </li>
          <li>
            C
            <ul>
              <li>
                B
              </li>
            </ul>
          </li>
        </ul>
      </li>
    </ul>
  </li>
</ul>

Demo (PHP 5.2-Variante)

Eine mögliche Variante wäre ein Iterator, der jedes RecursiveIterator durchläuft und eine Iteration über alle Ereignisse bereitstellt, die auftreten können. Ein Switch/Case innerhalb der foreach-Schleife könnte sich dann mit den Ereignissen befassen.

Verbunden:

27
hakre

Nun, zuerst würde ich das gerade Feld der Schlüssel-Wert-Paare in ein hierarchisches Feld umwandeln

function convertToHeiarchical(array $input) {
    $parents = array();
    $root = array();
    $children = array();
    foreach ($input as $item) {
        $parents[$item['id']] = &$item;
        if ($item['parent_id']) {
            if (!isset($children[$item['parent_id']])) {
                $children[$item['parent_id']] = array();
            }
            $children[$item['parent_id']][] = &$item;
        } else {
            $root = $item['id'];
        }
    }
    foreach ($parents as $id => &$item) {
        if (isset($children[$id])) {
            $item['children'] = $children[$id];
        } else {
            $item['children'] = array();
        }
    }
    return $parents[$root];
}

Das kann ein flaches Array mit parent_id und id in ein hierarchisches Array umwandeln:

$item = array(
    'id' => 'A',
    'blah' => 'blah',
    'children' => array(
        array(
            'id' => 'B',
            'blah' => 'blah',
            'children' => array(
                array(
                    'id' => 'C',
                    'blah' => 'blah',
                    'children' => array(),
                ),
             ),
            'id' => 'D',
            'blah' => 'blah',
            'children' => array(
                array(
                    'id' => 'E',
                    'blah' => 'blah',
                    'children' => array(),
                ),
            ),
        ),
    ),
);

Dann erstellen Sie einfach eine Renderfunktion:

function renderItem($item) {
    $out = "Your OUtput For Each Item Here";
    $out .= "<ul>";
    foreach ($item['children'] as $child) {
        $out .= "<li>".renderItem($child)."</li>";
    }
    $out .= "</ul>";
    return $out;
}
8
ircmaxell

Die Lösung von Alexander-Konstantinov mag auf den ersten Blick nicht so einfach zu lesen sein, sie ist jedoch sowohl genial als auch exponentiell besser in Bezug auf die Leistung, aber dies hätte als beste Antwort gewählt werden müssen.

Vielen Dank, Herr Kumpel, ich habe Ihnen zu Ehren einen Benchmark gemacht, um die beiden in diesem Beitrag vorgestellten Lösungen zu vergleichen.

Ich hatte einen flachen @ 250k-Baum mit 6 Ebenen, den ich konvertieren musste. Ich suchte nach einer besseren Methode, um rekursive Iterationen zu vermeiden.

Rekursion vs. Referenz:

// Generate a 6 level flat tree
$root = null;
$lvl1 = 13;
$lvl2 = 11;
$lvl3 = 7;
$lvl4 = 5;
$lvl5 = 3;
$lvl6 = 1;    
$flatTree = [];
for ($i = 1; $i <= 450000; $i++) {
    if ($i % 3 == 0)  { $lvl5 = $i; $flatTree[$lvl6] = $lvl5; continue; }
    if ($i % 5 == 0)  { $lvl4 = $i; $flatTree[$lvl5] = $lvl4; continue; }
    if ($i % 7 == 0)  { $lvl3 = $i; $flatTree[$lvl3] = $lvl2; continue; }
    if ($i % 11 == 0) { $lvl2 = $i; $flatTree[$lvl2] = $lvl1; continue; }
    if ($i % 13 == 0) { $lvl1 = $i; $flatTree[$lvl1] = $root; continue; }
    $lvl6 = $i;
}

echo 'Array count: ', count($flatTree), PHP_EOL;

// Reference function
function treeByReference($flatTree)
{
    $flat = [];
    $tree = [];

    foreach ($flatTree as $child => $parent) {
        if (!isset($flat[$child])) {
            $flat[$child] = [];
        }
        if (!empty($parent)) {
            $flat[$parent][$child] =& $flat[$child];
        } else {
            $tree[$child] =& $flat[$child];
        }
    }

    return $tree;
}

// Recursion function
function treeByRecursion($flatTree, $root = null)
{
    $return = [];
    foreach($flatTree as $child => $parent) {
        if ($parent == $root) {
            unset($flatTree[$child]);
            $return[$child] = treeByRecursion($flatTree, $child);
        }
    }
    return $return ?: [];
}

// Benchmark reference
$t1 = microtime(true);
$tree = treeByReference($flatTree);
echo 'Reference: ', (microtime(true) - $t1), PHP_EOL;

// Benchmark recursion
$t2 = microtime(true);
$tree = treeByRecursion($flatTree);
echo 'Recursion: ', (microtime(true) - $t2), PHP_EOL;

Die Ausgabe spricht für sich:

Array count: 255493
Reference: 0.3259289264679 (less than 0.4s)
Recursion: 6604.9865279198 (almost 2h)
5
ntt

Um in ULs und LIs zu parsen, wäre das ungefähr so:

$array = array (
    'H' => 'G'
    'F' => 'G'
    'G' => 'D'
    'E' => 'D'
    'A' => 'E'
    'B' => 'C'
    'C' => 'E'
    'D' => 'NULL'
);


recurse_uls ($array, 'NULL');

function recurse_uls ($array, $parent)
{
    echo '<ul>';
    foreach ($array as $c => $p)  {
        if ($p != $parent) continue;
        echo '<li>'.$c.'</li>';
        recurse_uls ($array, $c);
    }
    echo '</ul>';
}

Aber ich würde gerne eine Lösung sehen, bei der Sie nicht so oft durch das Array iterieren müssen ...

2
arnorhs

Folgendes habe ich mir ausgedacht:

$arr = array(
            'H' => 'G',
            'F' => 'G',
            'G' => 'D',
            'E' => 'D',
            'A' => 'E',
            'B' => 'C',
            'C' => 'E',
            'D' => null );

    $nested = parentChild($arr);
    print_r($nested);

    function parentChild(&$arr, $parent = false) {
      if( !$parent) { //initial call
         $rootKey = array_search( null, $arr);
         return array($rootKey => parentChild($arr, $rootKey));
      }else { // recursing through
        $keys = array_keys($arr, $parent);
        $piece = array();
        if($keys) { // found children, so handle them
          if( !is_array($keys) ) { // only one child
            $piece = parentChild($arr, $keys);
           }else{ // multiple children
             foreach( $keys as $key ){
               $piece[$key] = parentChild($arr, $key);
             }
           }
        }else {
           return $parent; //return the main tag (no kids)
        }
        return $piece; // return the array built via recursion
      }
    }

ausgänge:

Array
(
    [D] => Array
        (
            [G] => Array
                (
                    [H] => H
                    [F] => F
                )

            [E] => Array
                (
                    [A] => A
                    [C] => Array
                        (
                            [B] => B
                        )    
                )    
        )    
)
2
Dan Heberden

So erstellen Sie eine dynamische Baumansicht und ein Menü

Schritt 1: Zuerst erstellen wir eine Treeview-Tabelle in der MySQL-Datenbank. Diese Tabelle enthält vier Spalten. ID ist die Aufgaben-ID und Name ist der Name der Aufgabe.

-
-- Table structure for table `treeview_items`
--

CREATE TABLE IF NOT EXISTS `treeview_items` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(200) NOT NULL,
  `title` varchar(200) NOT NULL,
  `parent_id` varchar(11) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1 AUTO_INCREMENT=7 ;

--
-- Dumping data for table `treeview_items`
--

INSERT INTO `treeview_items` (`id`, `name`, `title`, `parent_id`) VALUES
(1, 'task1', 'task1title', '2'),
(2, 'task2', 'task2title', '0'),
(3, 'task3', 'task1title3', '0'),
(4, 'task4', 'task2title4', '3'),
(5, 'task4', 'task1title4', '3'),
(6, 'task5', 'task2title5', '5');

Schritt 2: Rekursive Methode für die Baumansicht Ich habe unterhalb der Methode createTreeView () die Methode tree angelegt, die rekursiv aufgerufen wird, wenn die aktuelle Task-ID größer ist als die vorherige Task-ID.

function createTreeView($array, $currentParent, $currLevel = 0, $prevLevel = -1) {

foreach ($array as $categoryId => $category) {

if ($currentParent == $category['parent_id']) {                       
    if ($currLevel > $prevLevel) echo " <ol class='tree'> "; 

    if ($currLevel == $prevLevel) echo " </li> ";

    echo '<li> <label for="subfolder2">'.$category['name'].'</label> <input type="checkbox" name="subfolder2"/>';

    if ($currLevel > $prevLevel) { $prevLevel = $currLevel; }

    $currLevel++; 

    createTreeView ($array, $categoryId, $currLevel, $prevLevel);

    $currLevel--;               
    }   

}

if ($currLevel == $prevLevel) echo " </li>  </ol> ";

}

Schritt 3: Erstellen Sie eine Indexdatei, um die Baumansicht anzuzeigen. Dies ist die Hauptdatei des Baumansichtsbeispiels. Hier rufen wir die createTreeView () - Methode mit den erforderlichen Parametern auf.

 <body>
<link rel="stylesheet" type="text/css" href="_styles.css" media="screen">
<?php
mysql_connect('localhost', 'root');
mysql_select_db('test');


$qry="SELECT * FROM treeview_items";
$result=mysql_query($qry);


$arrayCategories = array();

while($row = mysql_fetch_assoc($result)){ 
 $arrayCategories[$row['id']] = array("parent_id" => $row['parent_id'], "name" =>                       
 $row['name']);   
  }
?>
<div id="content" class="general-style1">
<?php
if(mysql_num_rows($result)!=0)
{
?>
<?php 

createTreeView($arrayCategories, 0); ?>
<?php
}
?>

</div>
</body>

Schritt 4: CSS-Datei erstellen style.cssHier schreiben wir alle CSS-bezogenen Klassen. Derzeit verwende ich die Bestellliste, um die Baumansicht zu erstellen. Sie können hier auch den Bildpfad ändern.

img { border: none; }
input, select, textarea, th, td { font-size: 1em; }

/* CSS Tree menu styles */
ol.tree
{
    padding: 0 0 0 30px;
    width: 300px;
}
    li 
    { 
        position: relative; 
        margin-left: -15px;
        list-style: none;
    }
    li.file
    {
        margin-left: -1px !important;
    }
        li.file a
        {
            background: url(document.png) 0 0 no-repeat;
            color: #fff;
            padding-left: 21px;
            text-decoration: none;
            display: block;
        }
        li.file a[href *= '.pdf']   { background: url(document.png) 0 0 no-repeat; }
        li.file a[href *= '.html']  { background: url(document.png) 0 0 no-repeat; }
        li.file a[href $= '.css']   { background: url(document.png) 0 0 no-repeat; }
        li.file a[href $= '.js']        { background: url(document.png) 0 0 no-repeat; }
    li input
    {
        position: absolute;
        left: 0;
        margin-left: 0;
        opacity: 0;
        z-index: 2;
        cursor: pointer;
        height: 1em;
        width: 1em;
        top: 0;
    }
        li input + ol
        {
            background: url(toggle-small-expand.png) 40px 0 no-repeat;
            margin: -0.938em 0 0 -44px; /* 15px */
            height: 1em;
        }
        li input + ol > li { display: none; margin-left: -14px !important; padding-left: 1px; }
    li label
    {
        background: url(folder-horizontal.png) 15px 1px no-repeat;
        cursor: pointer;
        display: block;
        padding-left: 37px;
    }

    li input:checked + ol
    {
        background: url(toggle-small.png) 40px 5px no-repeat;
        margin: -1.25em 0 0 -44px; /* 20px */
        padding: 1.563em 0 0 80px;
        height: auto;
    }
        li input:checked + ol > li { display: block; margin: 0 0 0.125em;  /* 2px */}
        li input:checked + ol > li:last-child { margin: 0 0 0.063em; /* 1px */ }

Mehr Details

0
user1364100