Navigation für JustPHP
Features einer automatischen Navigation
Die Navigations-Struktur in der linken Spalte von JustPHP wird händisch in top.php angelegt. Dies ist für den Anfang und bei wenigen Seiten, die so verlinkt werden durchaus ausreichend. Sobald die Anzahl der Seiten und die Verschachtelungstiefe ein gewisses Ausmass erreichen, ergeben sich mehrere Probleme. Einige davon sind.
- Immer die gesamte Navigation anzuzeigen ist bei grossen und tiefen Navigationen sehr unübersichtlich.
- Bei grossen Navigationen ist nicht mehr ersichtlich, wo sich die aktuelle Seite in der Navigation befindet.
Ich möchte deshalb hier eine dynamische Navigation entwickeln, die zwar immer noch sehr einfach ist, aber doch schon deutlich mehr Features enthält, wie eine rein statische Navigation.
Features:
- Eine Breadcrumb (Brotkrumen) Navigation soll mir anzeigen, in welchem Teil der Navigation ich mich befinde.
- Die angezeigte Navigation soll nur die Navigation von der Elternseite der aktuellen Seite abwärts im Baum anzeigen. Von der Elternseite aus, damit auch die Seiten auf derselben Ebene wie die aktuelle Seite noch angezeigt werden.
- Um nicht durch zu tiefe Verschachtelung irritiert zu werden, soll die dargestellte Tiefe der Navigation auf 3 Level beschränkt werden.
- Um anzuzeigen, welches die aktuelle Seite ist, wird die aktuelle Seite keinen Link enthalten und dadurch markiert sein. (Diese Tatsache kann man dann mittels CSS noch deutlicher hervorheben).
Damit die nun dargestellte Navigation funktioniert müssen die Unterseiten auf eine bestimmte Art angelegt werden. Am einfachsten erkläre ich das an einem Beispiel.
Will ich eine neue Seite mit dem Titel Testseite anlegen, kopiere ich die Datei template.php benenne die Kopie um in testseite.php und lege sie in dasselbe Verzeichnis wie die index.php. Will ich eine Unterseite zu dieser Seite anlegen erzeuge ich im selben Verzeichnis ein Unterverzeichnis mit dem namen testseite. In diese lege ich Unterseiten von testseite.php genauso an, wie die Testseite. Wenn ich Material in die Testseite einbinde (Bilder, Download-Material, ...) kommt dies ebenfalss in das Unterverzeichnis testseite. Auf diese Weise bekomme ich eine übersichtliche Struktur, die ich in der folgenden Navigations-Klasse verwenden kann, um die dynamische Navigation aufzubauen.
top.php
Damit die automatische Navigation funktioniert, muss die Datei top.php, die ja bisher die statische Navigation enthält, geändert werden. Die neue Datei enthält die Einbindung der Klasse Navigation.php sowie deren Verwendung, um die Navigation zu erzeugen. Wie die Klasse Navigation funktioniert, wird im nächsten Kapitel erklärt.
Wenn man das Original zum Vergleich sieht, kann man gut die Veränderungen sehen. Oben wird zuerst die Navigations-Klasse eingebunden, damit sie im späteren Verlauf verwendet werden kann. Im HTML-Code unten ist die statische Navigation durch die Aufrufe der Navigations-Funktionen ersetzt worden, die die Navigation erzeugen.
Bevor ein Navigations-Objekt erzeugt werden kann, muss noch die Navigations-Struktur als PHP-Array definiert werden. Ich hätte zwar auch eine Funktion schreiben können, die die Verzeichnisstruktur automatisch absucht und daraus den Navigations-Baum erzeugt. Aus verschiedenen Gründen habe ich mich gegen diese Möglichkeit entschieden.
- Im Verzeichnis-Baum sind unter Umständen Dateien (z.B. Bilder, Sound-Dateien, Download-Material), die nicht im Navigations-Baum dargestellt werden sollen. Diese sind bei einer automatischen Suche schwer auszuschliessen.
- Bei einer automatischen Suche kann ich die Reihenfolge der Dateien nicht festlegen. Wenn ich z.B. die Kapitel eines Buches in einem Verzeichnis ablege, so würfelt mir eine automatische Suche die Kapitel wild durcheinander, weil eine alphabetische Sortierung meist nicht die korrekte Reihenfolge angibt.
Aus diesen und anderen Gründen habe ich mich dazu entschieden, die Struktur manuell zu definieren. Die Struktur ist dabei möglichst einfach gehalten und folgendermassen aufgebaut.
- Die Navigation ist eine Liste von Arrays, jeder Eintrag der Liste ist Eintrag in der Navigation. Ein solcher Eintrag hat die Optionen label und url. label enthält den dargestellten Text des Eintrags und url den Namen der Datei, auf die verwiesen wird. url kann auch einen externen Link erhalten (dieser wird daran erkannt dass er mit http beginnt. Also möglichst keine Dateinamen mit http beginnen ;-) ). Hat ein Eintrag Unterseiten hat dieser Eintrag noch die Option children. Dies ist wiederum eine Liste von Einträgen.
- Diese Struktur kann beliebig tief geschachtelt sein.
<!DOCTYPE html> <?php date_default_timezone_set('Europe/Berlin'); require_once("classes/load.php"); $navigation = [ ["label" => "Programmieren", "url" => "programmieren.php", "children" => [ ["label" => "Konzepte", "url" => "konzepte.php"], ["label" => "Sprachen", "url" => "sprachen.php"], ["label" => "Werkzeuge", "url" => "werkzeuge.php", "children" => [ ["label" => "Git", "url" => "git.php"], ], ], ], ], ["label" => "Betriebssysteme", "url" => "betriebssysteme.php"], ]; $nav = new Navigation($navigation); ?> <html lang="en"> <head> <meta charset="utf-8"> <script type='text/javascript' src='/js/jquery.min.js'></script> <script src="/js/jqplot/jquery.jqplot.min.js"></script> <script type='text/javascript' src='/js/erichweigand.js'></script> <title><?php echo $title ? $title : 'Herzlich Willkomen bei Erich Weigand'; ?></title> <link rel="stylesheet" type="text/css" href="/js/jqplot/jquery.jqplot.min.css" /> <link rel='stylesheet' id='ewstyle-css' href='/css/erichweigand.css' type='text/css' media='all' /> </head> <body> <div class="navbar-top"> <a class="navbar-brand" href="/">Herzlich Willkommen</a> <ul class="navbar-nav"> <li><a href="/programmieren.php">Programmieren</a> | </li> <li><a href="/betriebssysteme.php">Betriebssysteme</a></li> </ul> </div> <div class="container" role="main"> <div class="col-nav"> <?php echo $nav->createBreadCrumb(); ?> <?php echo $nav->createNavigation(); ?> </div> <div class="col-content">
classes/Navigation.php
Diese Datei muss mit der Datei load.php eingebunden werden.
<?php class Navigation { private $breadcrumb = []; private $maxDepth = 2; private $navigation; private $pageUrl; private $subNavigation; /** * Konstruktor erhält die Datenstruktur für die Navigation */ public function __construct($navigation) { $this->pageUrl = $_SERVER["SCRIPT_NAME"]; $this->navigation = $this->initNavigation($navigation, "/", []); // Wurde keine SubNavigation gefunden stelle die ganze Navigation dar. if(!$this->subNavigation) { $this->subNavigation = $this->navigation; } } /** * Diese Funktion ersetzt die URL im Navigations-Array durch die vollständige URL und erstellt * ein Array für die Brotkrumen-Navigation. */ private function initNavigation($navigation, $basePath, $breadcrumb) { foreach ($navigation as $index => $item) { // Wenn kein externer link angegeben, interner Link basteln. if (!preg_match('/^http/', $item["url"])) { $item["url"] = $basePath . $item["url"]; } // Dies ist die aktuelle Seite, also gesammelte Brotkrumen und Subnavigation merken. if ($this->pageUrl == $item["url"]) { $this->subNavigation = &$navigation; $this->breadcrumb = $breadcrumb; } if (isset($item["children"])) { $childBase = preg_replace('/.php$/', '', $item["url"]) . '/'; $childBc = $breadcrumb; $childBc[] = ["url" => $item["url"], "label" => $item["label"]]; $item["children"] = $this->initNavigation($item["children"], $childBase, $childBc); } $navigation[$index] = $item; } return $navigation; } /** * Baut einen String auf, der die HTML-Struktur der Navigation enthält. * Wird rekursiv aufgerufen, um einen beliebig tiefen Baum darstellen zu können. * * @param array|null $navigation enthält den Teilbaum, der bei diesem Aufruf gerendert wird. * @param int $depth aktuelle tiefe der Rekursion * @return string der fertig gerenderte HTML-Baum */ public function createNavigation($navigation = null, $depth = 0) { // kein Wert in $navigation -> Das Rendering wird gerade begonnen -> zuerst die benötigte Baumstruktur besorgen. if (!$navigation) { $navigation = $this->subNavigation; } $liTagList = ""; foreach ($navigation as $item) { $liTag = '<a href="' . $item["url"] . '">' . $item["label"] . "</a>\n"; // Für die aktuelle Seite keinen Link. if ($this->pageUrl == $item["url"]) { $liTag = $item["label"]; } if (isset($item["children"]) && ($this->maxDepth > $depth)) { $liTag .= $this->createNavigation($item["children"], $depth + 1); } $liTagList .= "<li>\n" . $liTag . "</li>\n"; } return "<ul>\n" . $liTagList . "</ul>\n"; } public function createBreadCrumb() { $breadcrumb = []; foreach ($this->breadcrumb as $item) { $breadcrumb[] = '<a href="' . $item["url"] . '">' . $item["label"] . "</a>"; } return implode(" > ", $breadcrumb); } }