Warum ist mein menü nur bis in die zweite Ebene schachtelbar?

Liebe/-r Experte/-in,
Ich arbeite gerade an einem CMS. Dabei habe ich aber das Problem, dass in meinem
Menü ich einträge nur zwei Ebenen tief verschachteln kann. Hier erst mal mein Quellcode:

<?php require("connect.inc.php");
$sql = "SELECT id, label, link\_url, parent\_id FROM dyn\_menu ORDER BY parent\_id, pos ASC"; $items = mysql\_query($sql); while ($obj = mysql\_fetch\_object($items)) { if ($obj-\>parent\_id == 0) { $parent\_menu[$obj-\>id]['label'] = $obj-\>label; $parent\_menu[$obj-\>id]['link'] = $obj-\>link\_url; } else { $sub\_menu[$obj-\>id]['parent'] = $obj-\>parent\_id; $sub\_menu[$obj-\>id]['label'] = $obj-\>label; $sub\_menu[$obj-\>id]['link'] = $obj-\>link\_url; $parent\_menu[$obj-\>parent\_id]['count']++; } } mysql\_free\_result($items); function dyn\_menu($parent\_array, $sub\_array, $qs\_val = "menu", $main\_id = "nav", $sub\_id = "subnav", $extra\_style = "foldout") { $menu = " \n"; foreach ($parent\_array as $pkey =\> $pval) { if (!empty($pval['count'])) { $menu .= " [".$pval['label']."](%255C%2522%2522.%2524pval%255B'link'%255D.%2522?%2522.%2524qs_val.%2522=%2522.%2524pkey.%2522%255C%2522) \n"; } else { $menu .= " [".$pval['label']."](%255C%2522%2522.%2524pval%255B'link'%255D.%2522%255C%2522) \n"; } if (!empty($\_REQUEST[$qs\_val])) { $menu .= " \n"; foreach ($sub\_array as $sval) { if ($pkey == $\_REQUEST[$qs\_val] && $pkey == $sval['parent']) { $menu .= " [".$sval['label']."](%255C%2522%2522.rebuild_link(%2524sval%255B'link'%255D,) \n"; } } $menu .= "\n"; } } $menu .= "\n"; return $menu; } function rebuild\_link($link, $parent\_var, $parent\_val) { $link\_parts = explode("?", $link); $base\_var = "?".$parent\_var."=".$parent\_val; if (!empty($link\_parts[1])) { $link\_parts[1] = str\_replace ("&", "##", $link\_parts[1]); $parts = explode ("##", $link\_parts[1]); $newParts = array (); foreach ($parts as $val) { $val\_parts = explode ("=", $val); if ($val\_parts[0] != $parent\_var) { array\_push($newParts, $val); } } if (count($newParts) != 0) { $qs = "&".implode("&", $newParts); } return $link\_parts[0].$base\_var.$qs; } else { return $link\_parts[0].$base\_var; } } ?\> Die Funktion rufe ich dann in jeder Seite so auf: echo dyn\_menu($parent\_menu, $sub\_menu, "menu", "nav", "subnav"); Meine Datenbank sieht so aus: CREATE TABLE `dyn_menu` ( `id` INT(11) NOT NULL AUTO\_INCREMENT, `label` VARCHAR(50) NOT NULL DEFAULT '', `link_url` VARCHAR(100) NOT NULL DEFAULT '#', `parent_id` INT(11) NOT NULL DEFAULT '0', `pos` INT(11) NOT NULL, PRIMARY KEY (`id`) ) TYPE=MyISAM; In der Datenbank gibt es ja die ID, die jeden Eintrag eindeutig identifiziert. Dazu kommt dann noch die Parrent\_ID, mit der die Einträge noch jedem anderen Eintrag zugeordnet werden können. In dem Script wird ja geprüft, ob die ID und die parent\_id übereinstimmen. Das Problem ist aber nun, dass ich eben damit nur Bäume mit jeweils einem Unterast bauen kann, sobald man tiefer schachteln will, legt das Script in die HTML Ausgabe nur ganz ans Ende der navigationsliste noch eine Variablenübergabe ohne Link hin. Wie bekommt man dieses Script jetzt so aufgebohrt, dass der wirklich alles rekursiv durchgeht und die Struktur quasi unendlich tief verschachtelbar wird? Ok, 4 Ebenen würden mir für mein Vorhaben ja schon reichen. Danke für jede Hilfe. Richard

Hallo Richard

ich beschreib dir das nur mal grob…

du musst mit mehreren functionen arbeiten, um eine rekursion zu bauen

  1. function navi()

  2. function getMenu( $startpunkt =‚0‘)

  3. function hasSubmenu()

  4. starten eines
    … zwischen den uls:
    abfrage aller datenbankeinträge der obersten ebene
    und also getMenu() (ohne angabe eines startpunktes wird der defaultfall benutzt … hier O

  5. getMenu returned ein Listenpunkt

###menütitle###
???
bevor das li schliesst, fragst du hasSubmenu ab, ob der Menüpunkt unterpunkte hat … ist die antwort ja … wird innerhalb des li ein neues
… geöffnet

dazwischen rufst du die Funktion getMenu(Startpunkt= ID des aktuellen Menüpunktes)

getMenu liefert dann die nächste Ebene und überprüft immer wieder jeden einzelnen Menüpunkt per hasSubmenu ob er children hat und ruft dann solange immer wieder getMenu auf, bis es keine Untermenüpunkte mehr gibt

ich hoffe, ich konnte dir helfen

mfg

Silvio

Liebe/-r Experte/-in,
Ich arbeite gerade an einem CMS. Dabei habe ich aber das

Als ob es davon nicht schon genug gäbe :wink:

Problem, dass in meinem
Menü ich einträge nur zwei Ebenen tief verschachteln kann.

Naja, deine Struktur mit $menu und $sub_menu sind ja auch nur 2 Ebenen. Ich habe jetzt leider nicht viel Zeit, das komplett zu schreiben, daher nur die Ansätze und ungetestet:
$menu[id] als Array mit dem Kram, den du hast, zusätzlich einem Feld „sub“, das alle Unterpunkte in einem Array enthält.

$menu=Array();
$mainmenu = Array();
$sql = „SELECT id, label, link_url, parent_id FROM dyn_menu ORDER BY parent_id, pos
ASC“;
$items = mysql_query($sql);
while ($obj = mysql_fetch_object($items)) {
$menu[$obj->id][‚label‘] = $obj->label;
$menu[$obj->id][‚link‘] = $obj->link_url;
$menu[$obj->id][‚parent‘] = $obj->parent_id;
if ($obj->parent_id != 0) {
$menu[$obj->parent_id][‚sub‘][] = $obj->id;
} else {
$mainmenu[] = $obj->id;
}
}

Das sollte dann so aussehen, das bei Menüpunkten ohne Unterpunkte das „sub“ nicht gesetzt ist, ansonsten ist es ein Array mit den IDs der (direkten) Unterpunkte. In $mainmenu stehen alle Punkte ohne parentid, also das Hauptmenü. Muss man das Ganze nur noch rekursiv ausgeben (vereinfachte Version, HTML-Kram bitte selbst basteln; Das Forum hat auch Probleme mit HTML-Tags, daher #ul#, #li#…):

function display_menu($pkt) { // Array mit Ids der Mneüpunkte
echo „#ul#“;
foreach($pkt as $id) {
echo „#li#“ . $menu[$id][‚label‘];
if (isset($menu[$id][‚sub‘])) display_menu($menu[$id][‚sub‘]);
echo „#/li#“;
}
echo „#/ul#“;
}
display_menu($mainmenu);

PS: Bäume in SQL speichern geht mit „nestet sets“ schöner… wirf das bei Interesse mal in Google

Hi.

Kannst du mir das bitte schnell coden, ich steig da nicht so ganz durch. Das Problem ist, dass ich die Funktion schon gestern hätte fertig haben sollen. Viel Zeit für rumprobieren habe ich nicht mehr. Das wär echt voll lieb von dir.

Thx

Richard

hi.

Du schriebst ja, dass du nicht viel Zeit hast. Mein Problem ist, dass ich noch nicht so fitt im PHP bin, dass ich nach deiner Anleitung was vernünftiges Zusammenbasteln könnte. Wenn du mehr Zeit hast, könntest du mir das bitte einfach coden, damit ich es bei mir testen kann und dich nur noch mit den Fehlern versorge, die das Script rauswirft.

Das wär echt super, du wärst voll meine Rettung.

LG

Richard

Hi Richard

danke für dein Vertrauen, aber mehr als den Ansatz der Lösung kann ich dir hier nicht geben und ist sicher auch nicht das Ziel dieser Plattform. Sorry, aber solche Anfragen könnte ich nur im Rahmen eines offiziellen Auftrags erledigen.

Grüße silvio

Hallo,

ich komme leider erst am Sonntag dazu, mir das mal anzuschauen.
Gib bitte Bescheid, falls es schon vorher gelöst sein sollte

André

Hi,
Du musst Dir ein 2-dimensionales Array bauen, und bei jedem Element abfragen:

Bin ich Parent?
Nein, wer ist mein Parent-Element und an welcher Position stehe ich im Unter-Menü?

Beispiel Datenbank:

CREATE TABLE IF NOT EXISTS menue (
id int(11) NOT NULL auto_increment,
name varchar(50) collate latin1_german1_ci NOT NULL,
parent int(11) NOT NULL,
level int(11) NOT NULL,
position int(11) NOT NULL,
link varchar(50) collate latin1_german1_ci NOT NULL,
titletext varchar(50) NOT NULL,
PRIMARY KEY (id)
) ENGINE=MyISAM

wobei „level“ eine Kontrollinstanz darstellt. Level 1 kann nur direkt unter Level 0 liegen, und position die Position innerhalb des Submenüs bestimmt, also

Parent: Sport (id=1, parent=0,level=0, position=1)

Kid_1: Ballspiele(id=2, parent=1, level=1, position =1 )

Kid_1.1: Fußball (id=3, parent=2, level=2, position=1)
Kid_1.2:Handball (id=4, parent=2, level=2, position=2)
Kid_1.3: Hockey (id=5, parent=2, level=2, position=3)

Kid_2: Kampfsport (id=6, parent=1, level=1, position=1)

Kid_2.1 Judo(id=7, parent=6,level=2, position=1
Kid_2.2 Karate(id=8, parent=6,level=2, position=1

Kid_2.2.1 Kontakt(id=9, parent=8,level=3, position=1
Kid_2.2.2 ohne K.(id=10, parent=8,level=3, position=1

Parent: Über mich (id=11,parent=0,level=0, position=2)

Kid_11: Bilder (id=12, parent=11, level=1, position=1)
Kid_12:Kontakt (id=13, parent=11, level=1, position=2)

Hoffe, das hilft Dir.

Bis auf das vergessene global $menu in der function tut das so, wie ich es geschrieben habe, siehe http://nopaste.info/18623ed2f9.html

Das entstehende
…- Menu kannst du dann mit CSS formatieren, oder halt im Code die passenden CSS - Klassen ergänzen.

Hi.

Vielen lieben dank nochmal. Aber was fehlt jetzt noch? Wo kommt das vergessene Global hin? So sieht es jetzt aus:

<?php $menu=Array();
$mainmenu = Array(); mysql\_connect("localhost","test1",""); mysql\_select\_db("test1"); $sql = "SELECT id, label, link\_url, parent\_id FROM dyn\_menu ORDER BY parent\_id, pos ASC"; $items = mysql\_query($sql); while ($obj = mysql\_fetch\_object($items)) { $menu[$obj-\>id]['label'] = $obj-\>label; $menu[$obj-\>id]['link'] = $obj-\>link\_url; $menu[$obj-\>id]['parent'] = $obj-\>parent\_id; if ($obj-\>parent\_id != 0) { $menu[$obj-\>parent\_id]['sub'][] = $obj-\>id; } else { $mainmenu[] = $obj-\>id; } } function display\_menu($pkt) { // Array mit Ids der Mneüpunkte global $menu; echo " "; foreach($pkt as $id) { echo "- [" . $menu[$id]['label'] . "](%255C%2522%2522)"; if (isset($menu[$id]['sub'])) display\_menu($menu[$id]['sub']); echo " "; } echo ""; } display\_menu($mainmenu); ?\> Thx nochmalo Richard

Hallo,
die Funktionen die du hierher kopiert hast, sind nicht vollständig. In Zeile 11-13 ist ersichtlich, daß alle Submenü-Strukturen verfügbar sind. Du müßtest den code posten der das array $sub_menu nutzt.

Hi, ich hatte es extra nicht hier gepostet, weil es da kaputt geht, weil das Forum die HTML-Tags ausführt…

Das global steht hier nach dem function schon drin, das Script von nopaste funktioniert…

Hi.

Danke, ja, es funktioniert so weit. Aber etwas fehlt jetzt noch: Und zwar werden jetzt auf jeder Seite immer alle Menüpunkte angezeigt. Mit CSS mag ich die nicht ausblenden, weil das Sprachausgabennutzern nichts bringt. Daher muss ich im Script eine Funktion einfügen, die die Ausgabe des Menüs dort unterbricht und alle uls und lis zumacht, wenn man an dem letzt geöffneten Ast angekommen ist. Wie muss man so was realisieren?

Danke schon mal.

Richard

Hi,

da gibt es mehrere Varianten, was willst du genau ?

_1
__1.1
__1.2
__1.3

Hi.

Ich hätte gerne, dass nur alle Punkte der obersten Ebene, dann alle des aktiven Pfades in listen verschachtelt, und schließlich noch die übrigen Punkte aus der obersten Ebene, also der ebene mit parent_id = 0 angezeigt werden.

Thx

Richard

Hallo.

So, ich habe jetzt rausgefunden, dass man mit:

$server_url = „http://“.$_SERVER[‚HTTP_HOST‘].$_SERVER[‚REQUEST_URI‘];

Den Pfad zum aktuell geöffneten Dokument rausbekommt. Jetzt müsste ich doch in irgend einer While Schleife ein If basteln, das abfragt, ob die link_url aus der Datenbank mit $server_url übereinstimmt und dann die Schleife stoppt. Dann müssten aber noch alle übrigen Einträge mit parent_id = 0 angezeigt werden. Wie realisiere ich das?

Beste Grüße

Richard

Hallo,

http://nopaste.info/b6443e85a5.html

Oben das „if“ zum erkennen der aktuellen Seite musst du ggfs. nochmal ändern, wie du oben geschrieben hast, in $curpage sollte die id der aktuellen Seite landen.

Alex

Hi und guten Abend.

Jetzt werden mir die Unterpunkte der Menüs gar nicht mehr angezeigt.

LG

Richard

Hallo,

der Knackpunkt ist, dass du nicht mit einer echten Rekursion arbeitest. Das erkennt man daran, dass du die Funktion „dyn_menu“ nicht rekursiv aufrufst. Rekursiv wäre es, wenn sich die Funktion „selbst aufrufen“ würde, Wenn also innerhalb der Funktion der Aufruf „dyn_menu“ stünde.

Jetzt die Rekursion durchzuarbeiten, würde zu lange dauern.

Mein einfacher Tipp, ein simples Menü mit beliebiger Ebenen-Anzahl hinzubekommen ist:
a) arbeite ohne Rekursion, indem du 4 Ebenen statisch vorgibst
b) arbeite mit einem Menü-Array, in dem du das Menü konfigurieren kannst. Dort kannst du die Reichenfolge und die Ebene festlegen und kannst auf Parent-IDs komplett verzichten.

Z.B.

$menue = array(
array(
‚title‘ => ‚Seite 1‘,
‚level‘ => 1,
),

array(
‚title‘ => ‚Seite 1.1‘,
‚level‘ => 2,
),

array(
‚title‘ => ‚Seite 1.2‘,
‚level‘ => 2,
),

array(
‚title‘ => ‚Seite 1.2.1‘,
‚level‘ => 3,
),

array(
‚title‘ => ‚Seite 2‘,
‚level‘ => 1,
),
);

LG

Ajo

Wenn dir nur die Hauptmenüpunkte angezeigt werden, stimmt halt die ID der aktuellen Seite in $curpage nicht. je nachdem, wie du das einbaust, musst du evtl. den Vergleich bei
if (stristr($_SERVER[‚REQUEST_URI‘], $obj->link_url)) $curpage = $obj->id;
ändern…