[PHP/MySQL] Dynamische Navigation aus einer MySQL Datenbank

digiTALE

Lt. Junior Grade
Registriert
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
 
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
 
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:
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
 
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
 
  • Gefällt mir
Reaktionen: Coffeejunky_1
Hallo, dieses Thread ist zwar schon etwas älter, hat mir aber trotzdem sehr geholfen.
Ich hatte ebenfalls ein Menu erstellt mit WHILE und hatte auch funktioniert, aber diese Code hier sind kürzer als meine.
Ich habe den Code so in meinem Script übernommen und wollte diesen nun erweitern und zwar haben bei mir einige Sublinks wiederum Sublinks also subsublinks.
Ich bastele nun schon seit über einer Woche daran rum und bekomme es einfach nicht hin das mir Subsub_links angezeit werden.

So sollte das Ergebnis sein, aber bei mir fehlen halt sie Subsubmenu


Menu 1
----Submenu 1 zu 1
----Submenu 2 zu 1
----Submenu 3 zu 1

Menu 2
------Submenu 1 zu 2
------Submenu 2 zu 2
------------Subsubmenu 1 zu 2 .2
------------Subsubmenu 2 zu 2 .2
------Submenu 3 zu 2

Menu 3
------Submenu 1 zu 3
------------Subsubmenu 1 zu 1 .3
------Submenu 2 zu 3
------Submenu 3 zu 3
------------Subsubmenu 1 zu 3 .3
------------Subsubmenu 2 zu 3 .3
------------Subsubmenu 3 zu 3 .3
------------Subsubmenu 4 zu 3 .3
------------Subsubmenu 5 zu 3 .3

Im Moment sieht es leider nur so aus, die Subsubmenu fehlen.

PHP:
<?php

/* Datenbankanmeldeinformationen. Angenommen, Sie führen den MySQL-Server mit der Standardeinstellung aus (Benutzer 'root' ohne Kennwort). */
define('DB_SERVER', 'localhost');
define('DB_USERNAME', 'root');
define('DB_PASSWORD', '');
define('DB_NAME', 'as_cms');

/* Versuche, eine Verbindung zur MySQL-Datenbank herzustellen */
$dblink = mysqli_connect(DB_SERVER, DB_USERNAME, DB_PASSWORD, DB_NAME);

$admin_nav = mysqli_query($dblink, ' SELECT `id`, `parent`, `prio`, `name` FROM `as_newlinkmenue` ORDER BY `parent` ASC, `prio` ASC');

$config = array();
while ($row = mysqli_fetch_assoc($admin_nav)) {
    // Nun können wir festlegen was mit den Variablen passieren soll
    $config[$row['parent']][$row['id']] = $row['name'];
}
 function dumpNodes($config, $parent=0)
  {
    echo "<ul>\n";
    foreach ($config[$parent] as $id => $name) {
      echo '<li>', htmlSpecialChars($name);
      if (isset($config[$id])) {
        dumpNodes($config, $id);
      }
      echo "</li>\n";
    }
    echo "</ul>\n";
  }
 
dumpNodes($config);
 

?>
 
Zuletzt bearbeitet:
Ich habe gerade versehentlich ein Submenu angelegt und in der DB wohl eine falsche Zuordnung angegeben und in der Ausgabe zeigt er mir diesen jetzt als Kindelement eines übergeordneten Kindelement an, genau wie ich es wollte. Es funktionierte wohl schon die ganze Zeit, habe es wohl nur nicht begriffen.
Ohne diese Forum und diesem Thread hier, hätte ich bis in aller Ewigkeit gesucht.
Danke
 
  • Gefällt mir
Reaktionen: floq0r
Jetzt benötige ich zu diesem Thema doch noch mal eure Hilfe.
Wie kann ich die Links als Links und Sublinks so formatieren, das ich wenn ich auf dem Hauptlink klicke sich die Sublinks öffnen? Ich möchte ein horizontales Linkmenu daraus erstellen, hab aber irgendwie einen Knoten im Kopf. Der Code in meinem obigen Beispiel ist unverändert.
 
Niemand mehr hier im Forum der helfen könnte? :hammer_alt:
Bin für jede Hilfe Bzw. Anregung dankbar.
 
Naja, vielleicht das Menü per JavaScript entweder in seiner Struktur verändern oder die Untermenüs sichtbar/unsichtbar machen? Oder beim Klick auf einen Menüpunkt die Untermenüpunkte per asynchronem Request nachladen, auch wenn das sicherlich nicht nötig wäre?
 
mental.dIseASe schrieb:
Naja, vielleicht das Menü per JavaScript entweder in seiner Struktur verändern oder die Untermenüs sichtbar/unsichtbar machen? Oder beim Klick auf einen Menüpunkt die Untermenüpunkte per asynchronem Request nachladen, auch wenn das sicherlich nicht nötig wäre?
Die Links werden ja alle angezeigt, inklusiv aller Sublings und derer Sublinks. Die Sublinks sollen erst per hover auf dem Hauptling angezeigt werden. Die Frage ist, wie formatiere ich bei diesem oben geposteten Code, die Links das diese per css so angezeigt werden wie ich es mir vorstelle? Die selbe Frage wurde hier von X_Burner bereits schon gestellt, leider ohne Ergebnis.
 
Zuletzt bearbeitet:
Hm 😥
Niemand hier im Forum der eventuell eine Idee hat wie das Problem gelöst werden könnte?
 
Ich denke, die meisten hätten bei dieser Ergänzung gern noch etwas mehr Eigenleistung. Ich bin zwar nicht der CSS-Mensch, aber ich bin nicht sicher, ob du das, was du tun möchtest, damit umsetzen kannst. Das Stichwort "JavaScript" hatte ich ja schon genannt. So hatte ich es zumindest vor etwa 20 Jahren mal in der Schule umgesetzt.
 
mental.dIseASe schrieb:
Ich denke, die meisten hätten bei dieser Ergänzung gern noch etwas mehr Eigenleistung. Ich bin zwar nicht der CSS-Mensch, aber ich bin nicht sicher, ob du das, was du tun möchtest, damit umsetzen kannst. Das Stichwort "JavaScript" hatte ich ja schon genannt. So hatte ich es zumindest vor etwa 20 Jahren mal in der Schule umgesetzt.
Werd das mir mal anschauen wie das mit java funktioniert. Bin erst seit 3 Monaten dabei und mit ca. 65 ist das nicht mehr so einfach mit dem lernen.
Ich hatte mal Scripte vor 25 Jahren geschrieben für das cms e107, das ist aber schon ewig lang her und das Meiste habe ich natürlich durch die lange Pausierung wieder vergessen.
Hast du irgendwie eine Idee wie man das mit java umsetzen könnte?
 
JavaScript != Java.

Soweit ich weiß, haben darstellbare Elemente CSS-Eigenschaften zur Sichtbarkeit, ich meine "display". Per JavaScript kannst du diese display-Eigenschaft dann verändern, um Krempel ein- oder auszublenden.

Das ist aber nicht mein Fachgebiet, ich bin tiefer im Backend unterwegs.
 
Ich denke, du hast nicht ganz verstanden worum es eigentlich geht. Wie man css anwendet ist mir schon klar.
Es geht um mehrdimensionale arrays die als anklickbare Links ausgegeben werden mit Eltern und Kind.
Das funktioniert auch so weit sehr gut. Bekommt aber ein Kindelement selber ein Kindelement, dann wird es problematisch.
Die Namen Elternelemente und deren Kindelemente und deren Kindelemente kommen aus einer DB und werden über eine While-Schleife aufgelistet.
Bei zwei Ebenen ist das kein Problem, da werden die Eltern und deren Kindelemente sauber angezeigt.
Kommt aber zu einem Kindelement ein weiteres Kindelement, so das der darüberliegende Kindelement zum Elternelement wird, funktioniert das ganze nicht mehr. Das Script das ich nun getestet habe, steht weiter oben, funktioniert sehr gut, genau wie ich es mir vorgestellt hatte.
Da aber in dem Script nur einmal das <li> Element vorkommt, alle anderen <li> Elemente werden durch die While-Schleife generiert, wird es schwierig diese mit css zu gestallten. Ich könnte mir nur vorstellen, das ich die aus der While-Schleife generierten <li> eine id mit gebe und über die id dann die <li>mit css formatieren kann. Andere Lösung sehe ich leider zur Zeit nicht.

mental.dIseASe schrieb:
JavaScript != Java.

Soweit ich weiß, haben darstellbare Elemente CSS-Eigenschaften zur Sichtbarkeit, ich meine "display". Per JavaScript kannst du diese display-Eigenschaft dann verändern, um Krempel ein- oder auszublenden.

Das ist aber nicht mein Fachgebiet, ich bin tiefer im Backend unterwegs.
Das weiß ich ja, das wende ich ja auch bereits in andere Anwendungen an. Aber wie willst du Elementen Eigenschaften vergeben wenn diese noch nicht vorhanden sind. und erst über While aus der DB ausgegeben werden?
Oh, du hast mich gerade auf eine super Idee gebracht, ich schreibe für die jeweiligen Elemente die Eigenschaften gleich in die DB. Und bei der DB Abfrage werden dann nicht nur die Elemente per While ausgegeben, sondern die Eigenschaften gleich mit.
 
Ich verstehe es nicht, es werden die Sublings nicht angezeigt.
Code:
<?php
require('../'.as_SYSTEM.'/include.php');

$admin_nav = mysqli_query($dblink, ' SELECT `id`, `parent`, `class`, `sortierung`, `name` FROM `as_newlinkmenue` ORDER BY `parent` ASC, `sortierung` ASC');
echo '<ul>';
while ($row = mysqli_fetch_assoc($admin_nav)) {
  
   $id=$row['id'];
   $parent=$row['parent'];
   $name=$row['name'];
   $class=$row['class'];
  
    if ($parent==0) {$hauptlinks=$name;
    if ($parent==$id) {$sublinks=$name;}
  
        echo '<li>'.$hauptlinks;
            echo '<ul>';
                    echo '<li>'.$sublinks.'</li>';
            echo '</ul>';
        echo '</li>';
   }   
}
 echo '</ul>';
?>

Sieht jemand den Fehler? Ich bin wohl so langsam blind geworden. :-)
 
Hab das Problem gelöst, der einfachste Weg ist oft der schwierigste.
Um Datensätze zu einem bestimmten Datensatz zu gruppieren, gibt es die Funktion bei der DB Abfrage GROUP BY und somit werden alle Sublinks gruppiert die zu einem Hauptlink gehören.
 
Zurück
Oben