[PHP/MySQL] Dynamische Navigation aus einer MySQL Datenbank

digiTALE

Lt. Junior Grade
Dabei seit
Juli 2004
Beiträge
270
Seid gegrüßt,

Ich bastle gerade an einem kleinen Projekt. Die Navigation soll aus der Datenbank eingelesen werden. Die Schwierigkeit gestaltet sich darin, das die Hauptmenues auch Untermenues besitzen!


Aufbau der Datenbank als Beispiel:

Code:
+-----------+----------------+
| Kategorie | Unterkategorie |
+-----------+----------------+
| Menue 1   | Untermenue 1   |
+-----------+----------------+
| Menue 1   | Untermenue 2   |
+-----------+----------------+
| Menue 1   | Untermenue 3   |
+-----------+----------------+
| Menue 2   | Untermenue 1   |
+-----------+----------------+
| Menue 2   | Untermenue 2   |
+-----------+----------------+
| Menue 3   | Untermenue 1   |
+-----------+----------------+
Soweit so gut, durch den Befehl "distinct" in der SELECT Abfrage gebe ich nur einmal die jeweilige Kategorie aus! Und dann kommt auch schon mein Problem, wie schaffe ich es, das die Navigation so aussieht?

Menue 1
  • Untermenue 1
  • Untermenue 2
  • Untermenue 3
Menue 2
  • Untermenue 1
  • Untermenue 2
Menue 3
  • Untermenue 1


Mein bisheriger Code sieht wie folgt aus:
PHP:
$MysqlServer = mysql_connect("$host","$user", "$pass") or die ("Datenbank nicht erreichbar");
mysql_select_db($daba, $MysqlServer) or die ("Datenbank nicht vorhanden");

$Abfragen_1  = "SELECT distinct Kategorie FROM $tabl";
$Ergebnis_1 = mysql_query($Abfragen_1) or die (mysql_error());

$Abfragen_2  = "SELECT * FROM $tabl";
$Ergebnis_2 = mysql_query($Abfragen_2) or die (mysql_error());

while ($row = mysql_fetch_object($Ergebnis_1)) {
	$klein = strtolower("$row->Kategorie");
	print "		<a href=\"index.php?navcat=" .$klein. "\">" .$row->Kategorie. "</a><br />\n";

	while ($fetch = mysql_fetch_object($Ergebnis_2)) {
		print "		&bull; <a href=\"index.php\">" .$fetch->Unterkategorie. "</a><br />\n";
	}
}
Mit dem Code komm ich gerademal auf diese Lösung:

Menue 1
  • Untermenue 1
  • Untermenue 2
  • Untermenue 3
  • Untermenue 1
  • Untermenue 2
  • Untermenue 1
Menue 2
Menue 3

Hoffe konnte mich verständlich genug ausdrücken und ihr erkennt mein Problem!

Jedenfalls komm ich nicht auf die Lösung, dieses Problem zu lösen!

Wer kann mir dabei weiterhelfen!?

Mfg digiTALE
 

Keita

Lt. Commander
Dabei seit
Dez. 2006
Beiträge
1.282
Es gibt verschiedene Wege, um Bäume zu verwalten, die gängiste Methode ist die parent-child-Relation:
Code:
+----+--------+------+----------------+
| ID | parent | prio | Name           |
+----+--------+------+----------------+
| 1  |  NULL  |   0  | Menue 1        |
+----+--------+------+----------------+
| 2  |  NULL  |   1  | Menue 2        |
+----+--------+------+----------------+
| 3  |  NULL  |   2  | Menue 3        |
+----+--------+------+----------------+
| 4  |    1   |   0  | Untermenue 1   |
+----+--------+------+----------------+
| 5  |    1   |   1  | Untermenue 2   |
+----+--------+------+----------------+
| 6  |    1   |   2  | Untermenue 3   |
+----+--------+------+----------------+
| 7  |    2   |   0  | Untermenue 1   |
+----+--------+------+----------------+
| 8  |    2   |   1  | Untermenue 2   |
+----+--------+------+----------------+
| 9  |    3   |   0  | Untermenue 1   |
+----+--------+------+----------------+
ID ist der numerische Primärschlüssel, der im Falle von MySQL als auto-increment-Feld angelgt wird, parent enthält die ID des Übergeordneten Navigationspunktes/Datensatzes, prio ist die Sortierreihenfolge inner- bzw. unterhalb eines Navigationspunktes aufsteigend von 0 und Name die Bezeichnung des Navigationspunktes.
Was sich zunächst umständlich und aufgebläht präsentiert, hat handfeste Vorteile:
  • durch den Primärschlüssel läßt sich jeder Datensatz eineindeutig bestimmen
  • durch Zugriff auf die parent ID lassen sich Kinder gezielt ermitteln
  • die Sortierreihenfolge läßt sich jederzeit durch Änderungen in der prio-Spalte ändern
Um die komplette Navigation zu ermitteln, reicht ein einziges SQL-Statement:
Code:
   SELECT ID,
          parent,
          prio,
          Name
     FROM mytable
 ORDER BY parent ASC,
          prio   ASC
Damit bekommst Du eine Liste aller Navigationspunkte sortiert nach übergeordnetem Element und Sortierpriorität:
Code:
  $query = 'SELECT ID,
                   IFNULL(parent, 0) AS parent,
                   prio,
                   Name
              FROM mytable
          ORDER BY parent ASC,
                   prio   ASC';
  $res = mysql_query($query);
  
  $nodes = array();
  while ($row = mysql_fetch_assoc($res))
    $nodes[$row['parent']][$row['ID']] = $row['Name'];
  mysql_free_result($res);
$nodes hat anschließend in etwa folgenden Aufbau:
Code:
  array(
    0 => array(
      1 => 'Menue 1',
      2 => 'Menue 2',
      3 => 'Menue 3'
    ),
    1 => array(
      4 => 'Untermenue 1',
      5 => 'Untermenue 2',
      6 => 'Untermenue 3'
    ),
    2 => array(
      7 => 'Untermenue 1',
      8 => 'Untermenue 2'
    ),
    3 => array(
      9 => 'Untermenue 1'
    )
  );
Jetzt brauchst Du nur noch dieses Array zu durchlaufen, genauer gesagt das erste Element von $nodes:
Code:
  echo "<ul>\n";
  foreach ($nodes[0] as $id => $name) {
    echo '<li>', htmlSpecialChars($name);
    if (isset($nodes[$id])) {
      echo "<ul>\n";
      foreach ($nodes[$id] as $cId => $cName) {
        echo '<li>', htmlSpecialChars($cName), "</li>\n";
      }
      echo "</ul>\n";
    }
    echo "</li>\n";
  }
  echo "</ul>\n";
Und voila, fertig ist der Baum. Natürlich hat diese Lösungen einen kleinen Haken: die Ausgabefunktionalität funktioniert nur dann, wenn es maximal zwei Ebenen gibt, sobald eines der "Untermenue"s ein Kindselement bekommt, wird es schlicht nicht berücksichtigt.
Um auch für zukünftige Erweiterungen gewappnet zu sein, sollte daher die Ausgabe in eine Funktion gekapselt werden, die sich rekursiv aufruft, sobald Kindselement existieren:
Code:
  function dumpNodes($nodes, $parent = 0)
  {
    echo "<ul>\n";
    foreach ($nodes[$parent] as $id => $name) {
      echo '<li>', htmlSpecialChars($name);
      if (isset($nodes[$id])) {
        dumpNodes($nodes, $id);
      }
      echo "</li>\n";
    }
    echo "</ul>\n";
  }
  
  dumpNodes($nodes);
Eine weitere Methode sind sog. nested trees, das sind sich selbst referenzierende Knoten eines Baumes, am Anfang eine recht komplexe Geschichte, aber sobald man die Funktionsweise durchschaut hat, kein Hexenwerk mehr. Nur der reale Nutzen ist etwas umstritten... :D

greetings & have fun,
Keita
 

digiTALE

Lt. Junior Grade
Ersteller dieses Themas
Dabei seit
Juli 2004
Beiträge
270
Uijui, scheint doch komplexer zu sein als ich dachte :D. Dann werd ich es mal anpacken. Danke für die Antwort!

digiTALE


EDIT:

Bekomme es einfach nicht hin. In der "Foreach" Anweisung gibt es eine Fehlermeldung!

Die Datenbank habe ich übernommen:
Code:
+----+--------+------+----------------+
| ID | parent | prio | Categorie    |
+----+--------+------+----------------+
| 1  |  NULL  |   0  | Menue 1        |
+----+--------+------+----------------+
| 2  |  NULL  |   1  | Menue 2        |
+----+--------+------+----------------+
| 3  |  NULL  |   2  | Menue 3        |
+----+--------+------+----------------+
| 4  |    1   |   0  | Untermenue 1   |
+----+--------+------+----------------+
| 5  |    1   |   1  | Untermenue 2   |
+----+--------+------+----------------+
| 6  |    1   |   2  | Untermenue 3   |
+----+--------+------+----------------+
| 7  |    2   |   0  | Untermenue 1   |
+----+--------+------+----------------+
| 8  |    2   |   1  | Untermenue 2   |
+----+--------+------+----------------+
| 9  |    3   |   0  | Untermenue 1   |
+----+--------+------+----------------+
Ausführung:
PHP:
$query = mysql_query("SELECT id, IFNULL(parent, 0) AS parent, prio, categorie FROM navigation ORDER BY parent ASC, prio ASC");

$nodes = array();

print "<ul>\n";
while ($row = mysql_fetch_assoc($query)) {

	$nodes[$row['parent']][$row['id']] = $row['categorie'];;

	foreach ($nodes[0] as $id => $categorie) {
		print "<li>" .$categorie;

		if (isset($nodes[$id])) {
			print "<ul>\n";

			foreach ($nodes[$id] as $cId => $cCategorie) {
				print "<li>" .$cCategorie. "</li>\n";
      }
      print "</ul>\n";
    }
    print "</li>\n";
  }

}
mysql_free_result($query);
 echo "</ul>\n";
Ich finde einfach keine Lösung!
 
Zuletzt bearbeitet:

Keita

Lt. Commander
Dabei seit
Dez. 2006
Beiträge
1.282
Nee, halt, du hast da was durcheinander gewürfelt ;)

Die while-Schleife dient lediglich dazu die ermittelten Datensätze in das Array $nodes zu packen, um später mit der foreach-Schleife die einzelnen Elemente durchlaufen zu können:

PHP:
$query = mysql_query("SELECT id, IFNULL(parent, 0) AS parent, prio, categorie FROM navigation ORDER BY parent ASC, prio ASC");

$nodes = array();

// $nodes mit Elementen füllen
while ($row = mysql_fetch_assoc($query)) {
  $nodes[$row['parent']][$row['id']] = $row['categorie'];
}
mysql_free_result($query);

// Beginn der Ausgabe
print "<ul>\n";

// die oberste Ebene durchlaufen (parent == 0)
foreach ($nodes[0] as $id => $categorie) {
  print "<li>" .$categorie;

  // wenn es Element mit parent == $id gibt...
  if (isset($nodes[$id])) {

    // Beginn 2. Ebene
    print "<ul>\n";

    // die 2. Ebene durchlaufen (parent == $id)
    foreach ($nodes[$id] as $cId => $cCategorie) {
      print "<li>" .$cCategorie. "</li>\n";
    }

    // Ende 2. Ebene
    print "</ul>\n";
  }

  print "</li>\n";
}

// Ende Ausgabe
echo "</ul>\n";
Have fun,
Keita
 

X_Burner

Newbie
Dabei seit
Feb. 2007
Beiträge
1
Nabend !

Also erst ma meinen Dank für dieses schöne Beispiel von Dir, Keita !!!

Hat jemand eine Idee, wie ich das Bsp erweitern muss, damit man
aus der Liste eine Navi wird, bei die Unterkategorien ausgeklappt werden,
wenn man draufklickt, und der Link die ID der Kategorie enthält ?

Ich denke so hat sich der Postingstarter das in etwa gedacht :$

Und einen Gruss an alle CB-User !!!

CU

X_Burner
 
Top