PHP Verschachtelung

Eagle-PsyX-

Commander
Registriert
Juni 2006
Beiträge
2.246
Stehe irgendwie auf dem Schlauch, vielleicht kann mir jemand Helfen.
Ich hätte gerne folgendes Array verschachtel sortiert:
Code:
Array (
    [0] => Array
        (
            [category_id] => 1
            [cat_title] => Level 1
            [level] => 0
        )

    [1] => Array
        (
            [category_id] => 2
            [cat_title] => Level 2a
            [level] => 1
        )

    [2] => Array
        (
            [category_id] => 4
            [cat_title] => Level 2a-3a
            [level] => 2
        )

    [3] => Array
        (
            [category_id] => 5
            [cat_title] => Level 2a-3b
            [level] => 2
        )

    [4] => Array
        (
            [category_id] => 3
            [cat_title] => Level 2b
            [level] => 1
        )
)
zu
Code:
Array (
    [0] => Array
        (
            [category_id] => 1
            [cat_title] => Level 1
            [level] => 0
            [children] = > Array 
                (
                    [0] => Array
                        (
                            [category_id] => 2
                            [cat_title] => Level 2a
                            [level] => 1
                            [children] => Array 
                                (
                                    [0] => Array
                                        (
                                            [category_id] => 4
                                            [cat_title] => Level 2a-3a
                                            [level] => 2
                                        )
                                        
                                    [1] => Array
                                        (
                                            [category_id] => 5
                                            [cat_title] => Level 2a-3b
                                            [level] => 2
                                        )
                                )
                        )
                    [1] => Array
                        (
                            [category_id] => 3
                            [cat_title] => Level 2b
                            [level] => 1
                        )
                )
        )
)
Müsste ja eigentlich nichtmal zu schwer sein -.- bin kaputt...
Da sollte ein foreach() mit etwas Logik ausreichen.
 
Zuletzt bearbeitet:
kannst Du den noch mal formatieren, man kann es kaum lesen.
 
Eagle-PsyX- schrieb:
Da sollte ein foreach() mit etwas Logik ausreichen.
Wenn du garantieren kannst, dass immer die Eltern-Elemente vor ihren Kindern im Array stehen, dann reicht ein foreach, sonst brauchst du 2. :p
Elegante Lösung mit 2 Durchläufen: http://blog.tekerson.com/2009/03/03/converting-a-flat-array-with-parent-ids-to-a-nested-tree/
Falls 2 Durchläufe zu viel sind und du Interesse hast, kann ich dir meine Lösung mit im besten Fall einem und im schlechtesten Fall zwei Durchläufen zeigen.
 
Zuletzt bearbeitet:
Es gibt keinerlei "parent_id" (!).
Das Ergebnis stammt aus einer SQL Datenbank und wird so sortiert bereits ausgegeben ;-)

Ergänzung:
Okay, wieviele Stufen es gibt scheint bei den zwei Durchläufen egal sein.

Ergänzung #2:
Was ich noch rausholen könnte wäre, ob und wieviele *Kinder* ein Element hat (aber nicht welche :) )
 
Zuletzt bearbeitet:
Eagle-PsyX- schrieb:
Es gibt keinerlei "parent_id" (!).
Das Ergebnis stammt aus einer SQL Datenbank und wird so sortiert bereits ausgegeben ;-)
Und was ist dann der level-Wert? Das sieht mir eher nach einem Parent-Child Baum als einem Nested Set aus?
Kann es sein, dass der letzte Knoten in deinem Ergebnis-Array eine Ebene weiter nach rechts muss?
 
Zuletzt bearbeitet:
Der level-Wert wird über das SQL-Statement erzeugt und zeigt auch im Prinzip die "Tiefe" an.
Ich bin mir sehr sicher, dass es Nested Sets ist, immerhin habe ich es ja implementiert. Habe mitterlerweile eine mögliche Zwischenlösung, indem ich vor der Erzeugung des Arrays schon verschachtle.
 
Ob das nun "level" oder "parent_id" heißt, ist irrelevant. Auf jeden Fall entspricht das ganz bestimmt nicht dem Nested Sets Modell. Google wird dir dabei helfen, den Unterschied zu sehen und zu verstehen.
 
Es mag ja sein, dass da datenbankseitig kein parent-child-Baum dahinter steckt, allerdings entsprechen deine beiden Arrays aus dem ersten Beitrag sehr wohl diesem Modell. Und das lässt sich über das in meinem Link beschriebene Verfahren in einen Array-Baum verwandeln.
 
Euch ist klar, das level nie auf eine category_id verweist?!
Jeglich ''Ähnlichkeit' ist hier dann Zufall.

Trainmaster schrieb:
Auf jeden Fall entspricht das ganz bestimmt nicht dem Nested Sets Modell.
Wenn du nicht helfen kannst, okay. Aber ich stelle hier keine Diskussionrunde auf für etwas, das nicht zu diskutieren ist! Welchem Model entspricht es dann bitteschön?

PHP:
$navigation['category']['query_string'] = "
    SELECT o.category_id, o.category_name_de AS cat_title, o.category_left, o.category_right,
         COUNT(p.category_id)-1 AS level,
         (SELECT COUNT(*) FROM `".$DataBase['content']."` AS text WHERE text.text_category = o.category_id) AS count_content,
         (SELECT COUNT(*) FROM `".$DataBase['gallery']."` AS gal WHERE gal.gal_category = o.category_id) AS count_gallery,
         (SELECT COUNT(*) FROM `".$DataBase['product']."` AS prod WHERE prod.product_category = o.category_id) AS count_product
    FROM `".$DataBase['category']."` AS n,
         `".$DataBase['category']."` AS p,
         `".$DataBase['category']."` AS o
    WHERE 
        o.category_left 
            BETWEEN p.category_left AND 
            p.category_right
        AND o.category_left 
            BETWEEN 
n.category_left AND 
n.category_right
        AND n.category_id = ".$nav['cat_id']."
    GROUP BY 
        o.category_left
    ORDER BY 
        o.category_left";
$navigation['category']['query'] = mysql_query($navigation['category']['query_string']);

if($navigation['category']['query']) {
   $collect = array();
   while($row = mysql_fetch_assoc($navigation['category']['query'])) {
    $collect[] = $row;
  }
}

Das Problem ist übrigens gelöst, danke an geodreieck24 der versucht hat zu helfen.
 

Anhänge

  • Unbenannt-1.png
    Unbenannt-1.png
    13,4 KB · Aufrufe: 201
Zuletzt bearbeitet:
Eagle-PsyX- schrieb:
Euch ist klar, das level nie auf eine category_id verweist?!
Jeglich ''Ähnlichkeit' ist hier dann Zufall.
Deshalb auch meine Nachfrage:
geodreieck24 schrieb:
Kann es sein, dass der letzte Knoten in deinem Ergebnis-Array eine Ebene weiter nach rechts muss?
Aber ok, dann hatte ich das falsch verstanden. ;) Schön, dass du es trotzdem hinbekommen hast. :daumen:
 
Hallo,

Also so wie ich das sehe muss der letzte array Eintrag auch nach dem Nested Sets System eingerückt werden. Aber zu deinem Problem, mit diesem array lässt sich das nicht lösen wie willst du die Einträge denn zuordnen wenn in dem array nicht mal category_left und category_right stehen lösen? Ansonsten würde ich das Problem mit einer rekursiven Funktion lösen.

LG
GreenGene
 
Ahh jetzt sehe ich erst was ihr mit dem Einrücken meint.

Meine Lösungsansatz ist nur eine Zwischenlösung. Wenn jemand von euch ein Skript bzw. Idee hat, das mit category_left & category_right zu lösen bin ich ganz Ohr.

Ergänzung:
Mit Hilfe von dem hier könnte ich das schaffen:
http://phpperformance.de/wie-erstelle-ich-eine-html-liste-aus-einem-nested-set/

Ergänzung #2:
PHP:
$n = toHierarchy(&$collect);

 function toHierarchy(&$collection) {
    $trees = array();
    $l = 0;

    if (count($collection) > 0) {
      $stack = array();
      foreach ($collection as $node) {
        $item = $node;
        $item['children'] = array();
        $l = count($stack);
        while ($l > 0 && $stack[$l-1]['depth'] >= $item['depth']) {
          array_pop($stack);
          $l--;
        }
        if ($l == 0) {
          $i = count($trees);
          $trees[$i] = $item;
          $stack[] = &$trees[$i];
        } else {
          $i = count($stack[$l-1]["children"]);
          $stack[$l-1]["children"][$i]=$item;
          $stack[]=&$stack[$l-1]["children"][$i];
        }
      }
    }
    return $trees;
  }
 
Zuletzt bearbeitet:
Eagle-PsyX- schrieb:
Welchem Model entspricht es dann bitteschön?

Ohne die Datenbankstruktur zu kennen, könnten die Arrays aus dem ersten Post gleichermaßen aus einem Parent-Modell entstanden sein.

Wo liegen nun die Schwierigkeiten, von deinem ausgelesenen Array zu dem hierarchischen Array zu gelangen? geodreieck24 hat dir in Beitrag #3 einen passenden Link gegeben.
 
man könnte das geschickt über Rekursion lösen:
PHP:
unflatten(array &$result, array $insert)

Da müssen dann nur 3 Fälle abgedeckt werden:
  1. $result ist leer: $result = array_shift($insert)
  2. array_shift($insert) ist Child eines Eintrages: als Child eintragen
  3. array_shift($insert) ist Vater eines Eintrages: als Vater eintragen
 
Das Problem mit dem level ist, dass damit nicht klar wird zu welchem Set etwas gehört, denn es gibt ja anscheinend nur die Tiefe aber keine Zugehörigkeit an.

Wenn ich heute Abend Zeit haben sollte schreib ich dir eine rekursive Funktion mit category_left & category_right zu schreiben. Ich weiß auch immer noch nicht wie man ohne diese Werte ein Zuordnung machen soll. Zu deinem Link das kannst du dir ja mal genau durchlesen. Ich habe es nur überflogen aber es sieht gut aus.
Ergänzung ()

Also so sollte es auch ohne category_left & right gehen unter der Vorraussetzung das nach category_left sortiert wird:

PHP:
function make_tree($array) {
    $tree = array();
    $cnt_array = count($array);
    for($i=0;$i<$cnt_array;$i++) {
      if(($i<$cnt_array-1) && ($array[$i]['level']<$array[$i+1]['level'])) {
        $untilIndexUnderThisKat = getKatsUnder($array,$i+1,$array[$i]['level']);
        $tree[] = make_tree(array_cut($array,$i+1,$untilIndexUnderThisKat));
        $i = $untilIndexUnderThisKat;
      } else {
        $tree[] = $array[$i];
      }
    }
    return $tree;
}

function getKatsUnder($array,$start,$ebene) {
    $cnt_array = count($array);
    for($i=$start;$i<$cnt_array;$i++) {
      if($array[$i]['level']==$ebene) return $i-1;
    }
    return ($cnt_array-1);
}

function array_cut($srcArr,$start,$end) {
    $newArr = array();
    for($i=$start;$i<=$end;$i++) {
      $newArr[] = $srcArr[$i];
    }
    return $newArr;
}

$tree = make_tree($collection);
 
Danke.

@Trainmaster
Wenn man etwas nicht weiß, dann sollte man nicht so tun als ob bzw. keinesfalls etwas unterstellen ;) Und nochmal: level gibt keine Zugehörigkeit an, nur die Tiefe.

Das Ergebnis seht ihr u.a. hier in der oberen Navigation:
Link
 
Zuletzt bearbeitet:
Bitte, dann hättest du das doch auch direkt ausgeben können, wenn du es für die Navigation brauchst oder nicht? :D Dann brauch man auch keine Rekursion sondern es geht wirklich mit einer einzigen Schleife.
 
Ich brauche ja auch keine SQL-Rekursion.
Wollte ich doch auch nie... Zudem ist nur ein Verwendungszweck des Arrays ;)

Bis dato wird für jede Kategorie einmal die Verschachtelung ausgehend vom Ast zum Blatt geholt (nicht Stamm). In Zukunft soll diese Abfrage nur ein einziges mal alles abgerufen werden und die notwendige Teilbereiche ausgewertet und dargestellt.
 
Zuletzt bearbeitet:
Ich meine ja auch die rekursive php Funktion, die ich gepostet habe, aber wenn du das array noch für andere Aufgaben benötigst lohnt sich das ganze ja doch.
 
Zurück
Oben