Problem mit mehrstufiger Navigation mit php

Einklappen
X
 
  • Filter
  • Zeit
  • Anzeigen
Alles löschen
neue Beiträge

  • Problem mit mehrstufiger Navigation mit php

    Hallo zusammen ich habe da ein hoffentlich nur kleines Problem. Und zwar geht es um eine mehrstufige Navigation. Ich habe das die Datei im Netz gefunden und bin schwer begeistert davon.
    Ich hätte nun lieber anstatt der zwei level, drei level.
    Also so sieht es nun aus:
    Code:
    1
    1.1
    1.2
    2
    2.1
    2.2
    2.3
    3
    4.1
    schön wäre es wenn sie so aussehen könnte
    Code:
    1.
    1.1
    1.1.1
    1.1.2
    2
    3
    4.1.1
    Ich hoffe mir kann einer bei diesem Problem helfen und Danke jetzt schon mal im voraus.

    Hier ist die .php Datei

    PHP-Code:
    <?php
    $siteRoot
    ="index.php?inhalt_mitte=";
    $menuStruct = array("Vorwort"=>array("root"=>$siteRoot."content/vorwort.inc.php""CSS Dateien strukturieren"=>$siteRoot."content/strukturieren.inc.php"),
                        
    "Eigenschaften"=>array("root"=>$siteRoot."content/eigenschaften.inc.php"),
                        
    "Aufgaben"=>array("root"=>$siteRoot."content/aufgaben.inc.php","Formularfelder"=>$siteRoot."content/formularfelder.inc.php","Listen"=>$siteRoot."content/listen.inc.php","Buttons"=>$siteRoot."content/buttons.inc.php")
                       );
    $aktuell $_GET["inhalt_mitte"];
    $url $siteRoot.$aktuell;
    foreach(
    $menuStruct as $key=>$value)
    {
      if(
    $url == $value['root'])
      {
        echo 
    "<a  class=\"fstLevelActive\" href=".$value['root'].">$key</a>\n";
      }
      else
      {
        echo 
    "<a class=\"fstLevel\" href=".$value['root'].">$key</a>\n";
      }
      foreach(
    $value as $key2=>$value2)
      {
        if(
    array_search($url,$value))
        {
          if(
    $key2 != "root")
          {
            if(
    $url == $value2)
            {
              echo 
    "<a class=\"secLevelActive\" href=".$value2.">$key2</a>\n";
            }
            else
            {
              echo 
    "<a class=\"secLevel\" href=".$value2.">$key2</a>\n";
            }
          }
        }
      }
    }
    ?>

  • #2
    Willkommen im Forum.

    Ich fürchte, das ist nicht so leicht, weil der Code ziemlich „speziell“ für die Aufgabe geschrieben ist, die er aktuell erfüllt.

    Etwa so was…

    Code:
    if(array_search($url,$value))
    …, das vermutlich bedeuten soll, dass… Ehrlich gesagt: Ich habe keine Ahnung, was das genau bedeuten soll. Untermenüs klappen sich wieder ein, sobald ein Element daraus ausgewählt wird, weil $url dann nicht mehr in $value liegt?

    Wie auch immer: Man kann veruschen, dir da irgendwie was reinzubasteln, aber das wird voraussichtlich auf Anhieb nicht so werden, wie du es haben willst. Und bevor das dann ein ewiges Hin und Her gibt, könnte man besser gleich ne ordentliche Lösung wählen, die etwa auch mit einer beliebigen Anzahl an Ebenen umgehen kann und die ordentlichen HTML-Code erzeugt (mit Listen).

    Kommentar


    • #3
      Verschachtelte Menüs, Nested Menus, Bäume und Trees, Walk, Traverse, ...

      Zitat von Theryl Beitrag anzeigen
      Hallo zusammen ich habe da ein hoffentlich nur kleines Problem. Und zwar geht es um eine mehrstufige Navigation. Ich habe das die Datei im Netz gefunden und bin schwer begeistert davon.
      Ich hätte nun lieber anstatt der zwei level, drei level.
      Prinzipiell könntest du einfach eine weitere foreach()-Ebene hinzufügen. Aber man kann die Aufgabenstellung auch generalisieren und beliebige Verschachtelungstiefen zulassen. Dazu benötigt man zwar Rekursion[0], dafür kommt man mit einer foreach()-Schleife aus.

      Hinzu kommt, dass hier (möglicherweise) vom Benutzer eingegebene Daten (Zeichenketten) in einen HTML-Zusammenhang gebracht werden. Das schreit nach HTML-Escaping. Leider unterstützt uns PHP dabei nicht konsequent, wir müssen also selbst darauf achten, keine einzige Variable zu vergessen. Und über ein ganzes Script verstreute HTML-Fragmente führen auch ganz schnell zu invalidem Markup.

      Ich hab da mal schnell was gebastelt. Die Formatierung ist getrennt vom Durchlaufen des Menü-Baums. Damit ist das HTML-Escaping an einer Stelle konzentriert, und die HTML-Fragmente ebenfalls.

      PHP-Code:
      interface MenuFormatter {
          function 
      formatList($children$level 0);
          function 
      formatItem(
              
      $children ''// rendered text
              
      $level 0// indentation level
              
      $href ''// url, path
              
      $desc null// description
              
      $selected false // whether this item is the current one or not
          
      );
      }

      class 
      HtmlListFormatter implements MenuFormatter {
          static function 
      esc($cdata) {
              
      // or whatever suits your needs
              
      return htmlspecialchars($cdataENT_QUOTES'UTF-8');
          }

          private 
      $styleClassMenu;
          private 
      $styleClassSelected;

          function 
      __construct() {
              
      // initialize vars
              
      $this->styleClassMenu();
              
      $this->styleClassSelected();
          }

          
      // template for html class attribute
          
      const cssClass ' class="%s"';

          
      // set class for the surrounding ul-element
          
      function styleClassMenu($class 'menu') {
              
      $this->styleClassMenu sprintf($this::cssClass$this::esc($class));
              return 
      $this;
          }

          
      // set class for selected item
          
      function styleClassSelected($class 'selected') {
              
      $this->styleClassSelected sprintf($this::cssClass$this::esc($class));
              return 
      $this;
          }

          const 
      nl "\n";
          const 
      indent '    ';

          const 
      listTemplate '%1$s<ul%2$s>%3$s%1$s</ul>';

          function 
      formatList(
              
      $children,
              
      $level 0
          
      ) {
              
      $indent $this::nl str_repeat($this::indent$level);
              return 
      sprintf(
                  
      $this::listTemplate,
                  
      $indent,
                  
      $level $this->styleClassMenu '',
                  
      $children
              
      );
          }

          const 
      itemTemplate '%s<li%s><a href="%s">%s</a>%s%s</li>';

          function 
      formatItem(
              
      $children '',
              
      $level 0,
              
      $href '',
              
      $desc null,
              
      $selected false
          
      ) {
              
      $indent $this::nl str_repeat($this::indent$level);
              return 
      sprintf(
                  
      $this::itemTemplate,
                  
      $indent,
                  
      $selected $this->styleClassSelected '',
                  
      $this::esc($href),
                  
      $this::esc($desc),
                  (string) 
      $children,
                  empty (
      $children) ? '' $indent
              
      );
          }
      }

      class 
      NestedMenu {
          static function 
      hasValidStructure(Array $hmenu) {
              
      // insert your menu structure validation code here
              // ...
              
      return true;
          }

          private 
      $selectedShortPath '';
          private 
      $fmtr// MenuFormatter::
          
      private $menu// array() menu structure

          
      function __construct(
              Array 
      $menu,
              
      MenuFormatter $fmtr null
          
      ) {
              if (!
      $this::hasValidStructure($menu)) {
                  throw new \
      Exception('invalid menu structure given');
              }
              
      $this->fmtr $fmtr ?? new HtmlListFormatter();
              
      $this->menu $menu;
          }

          function 
      __toString() {
              return (string) 
      $this->render($this->menu);
          }

          
      // set the class names in the HtmlListFormatter:: class (if available)
          
      function styleClass($what$name) {
              if (!
      method_exists($this->fmtr$func "styleClass$what")) {
                  throw new \
      Exception('invalid style class given');
              }
              return 
      $this->fmtr->$func($name);
          }

          
      // set the current short path, so the MenuFormatter:: can highlight it
          
      function selectedShortPath($spath) {
              
      $this->selectedShortPath = (string) $spath;
              return 
      $this;
          }

          
      // get valid URL or localpath from given short path
          
      function buildPath($short_path) {
              return 
      $short_path;
          }

          const 
      parentName '.';

          
      /// return something which is convertible to string
          
      function render(
              Array 
      $nodes// nested Array; for structure see usage example below
              
      $level /// int(0...)
          
      ) {
              
      $out '';
              foreach (
      $nodes as $name => $node) {
                  if (
      $this::parentName === $name) {
                      continue;
                  }
                  if (empty (
      $node) && is_array($node)) {
                      continue; 
      // empty lists make no sense, so we do not render them
                  
      }
                  
      $short_path is_array($node)
                      ? 
      $node[$this::parentName] ?? ''
                      
      $node;
                  
      $out .= $this->fmtr->formatItem(
                      
      is_array($node) ? $this->render($node$level 2) : '',
                      
      $level 1,
                      
      $this->buildPath($short_path),
                      
      $name,
                      
      $short_path === $this->selectedShortPath
                  
      );
              }
              if (!isset (
      $out[0])) {
                  return 
      null// skip empty lists
              
      }
              return 
      $this->fmtr->formatList($out$level);
          }

      Die Anwendung ist simpel. Ein Beispiel:
      PHP-Code:
      $from = array (
          
      'Vorwort' => array (
              
      '.' => 'vorwort.inc.php',
              
      'CSS Dateien strukturieren' => 'strukturieren.inc.php',
          ),
          
      'Eigenschaften' => array (
              
      '.' => 'eigenschaften.inc.php',
          ),
          
      'Aufgaben' => array (
              
      '.' => 'aufgaben.inc.php',
              
      'Formularfelder' => array (
                  
      '.' => 'formularfelder.inc.php',
                  
      'Texteingabefelder' => 'textinput.inc.php',
                  
      'Optionslisten' => 'optionen.inc.php',
              ),
              
      'Listen' => 'listen.inc.php',
              
      'Buttons' => 'buttons.inc.php',
          ),
      );
      $menu = new NestedMenu($from);
      $menu->selectedShortPath('formularfelder.inc.php');
      $menu->styleClass('Selected''active'); 
      echo 
      $menu;

      // dump html source
      echo '<pre>'HtmlListFormatter::esc($menu), '</pre>'
      Damit die Links auch "funktionieren", musst du noch die Funktion ->buildPath() anpassen (oder besser in einer abgeleiteten Klasse überschreiben). Wenn du einmal dabei bist, kannst du auch gleich die "short-paths" im Menü-Array um die wiederholten ".inc.php" kürzen.
      ... und darüber nachdenken, ob es sinnvoll ist, Inhalte als PHP-Dateien einzubinden, statt nur als HTML.

      Aus deinem "root"-Key hab ich "." gemacht. Das erschien mir sinnvoller: Root ist das Wurzelverzeichnis, nicht das übergeordnete oder aktuelle. Außerdem tippt sich das schneller. Wenn dir das nicht gefällt, musst du die Konstante :: parentName ändern.

      Spezielle visuelle Effekte (wie Hervorhebung oder Einklappen benachbarter Untermenüs) lassen sich über CSS realisieren. Dazu enthält das erzeugte HTML (meiner Meinung nach) ausreichend Kennzeichnungen durch class-Attribute.

      Das Implementieren einer Überprüfung der Menüstruktur auf formale Korrektheit in ::hasValidStructure() ist als Übungsaufgabe gedacht.

      --
      [0] Ich wollte da eigentlich einen Wikipedia-Artikel verlinken, aber zumindest die beiden deutschsprachigen Artikel (Rekursion, Rekursive_Programmierung) sind vollkommen nutzlos, um das rekursive "Durchforsten" von Baum-Strukturen (oder verschachtelten Listen) zu verstehen.
      Zuletzt geändert von fireweasel; 18.01.2016, 21:50. Grund: korrigierte li-ul-Verschachtelung + ::parentName im Fließtext wurde zum :-P-Smiley ...
      Klingon function calls do not have “parameters”‒they have “arguments”‒and they always win them!

      Kommentar


      • #4
        Eine weitere Variante von mir:
        PHP-Code:
        class NavigationNode {
            public 
        $title;
            public 
        $url;
            public 
        $children;
            
            public function 
        __construct($title ''$url '', array $children = []) {
                
        $this->title $title;
                
        $this->url $url;
                
        $this->children $children;
            }

        PHP-Code:
        class NavigationHtmlListRenderer {
            private 
        $rootNode;
            private 
        $charset;
            
            public function 
        __construct(NavigationNode $rootNode$charset 'utf-8') {
                
        $this->rootNode $rootNode;
                
        $this->charset $charset;
            }
            
            public function 
        render($activeUrl ''$formatOutput false) {
                
        $dom = new DOMDocument('1.0'$this->charset);
                
                
        $rootElement $this->renderNode($dom$this->rootNode);
                
                
        $listElement $dom->createElement('ul');
                
        $listElement->appendChild($rootElement);
                
        $rootElement->appendChild($this->renderChildren($dom$this->rootNode));
                
                
        $dom->appendChild($listElement);
                
                foreach (
        $dom->getElementsByTagName('a') as $linkElement) {
                    if (
        $linkElement->getAttribute('href') === $activeUrl) {
                        
        $linkElement->setAttribute('class''active');
                    }
                }
                
                if (
        $formatOutput) {
                    
        $dom->formatOutput true;
                }
                return 
        $dom->saveXML($listElement);
            }
            
            private function 
        renderNode(DOMDocument $domNavigationNode $node) {
                
        $linkElement $dom->createElement('a'$node->title);
                
        $linkElement->setAttribute('href'$node->url);
                
                
        $listItemElement $dom->createElement('li');
                
        $listItemElement->appendChild($linkElement);
                
                return 
        $listItemElement;
            }
            
            private function 
        renderChildren(DOMDocument $domNavigationNode $parentNode) {
                
        $listElement $dom->createElement('ul');
                
                foreach (
        $parentNode->children as $node) {
                    
        $listItemElement $this->renderNode($dom$node);
                    if (!empty(
        $node->children)) {
                        
        $listItemElement->appendChild($this->renderChildren($dom$node));
                    }
                    
                    
        $listElement->appendChild($listItemElement);
                }
                
                return 
        $listElement;
            }

        PHP-Code:
        $navigation = new NavigationNode('Home''/', [
            new 
        NavigationNode(
                
        'Main''/main', [
                    new 
        NavigationNode(
                        
        'Main Page 1''/main/1'
                    
        ),
                    new 
        NavigationNode(
                        
        'Main Page 2''/main/2'
                    
        ),
                    new 
        NavigationNode(
                        
        'Main Page 3''/main/3', [
                            new 
        NavigationNode(
                                
        'Sub Page 1''/main/3/1'
                            
        ),
                            new 
        NavigationNode(
                                
        'Sub Page 2''/main/3/2'
                            
        )
                        ]
                    )
                ]
            ),
            new 
        NavigationNode(
                
        'Contact''/contact'
            
        )
        ]);

        $renderer = new NavigationHtmlListRenderer($navigation);
        echo 
        $renderer->render('/main/3/1'true); 
        Ausgabe:
        HTML-Code:
        <ul>
          <li>
            <a href="/">Home</a>
            <ul>
              <li>
                <a href="/main">Main</a>
                <ul>
                  <li>
                    <a href="/main/1">Main Page 1</a>
                  </li>
                  <li>
                    <a href="/main/2">Main Page 2</a>
                  </li>
                  <li>
                    <a href="/main/3">Main Page 3</a>
                    <ul>
                      <li>
                        <a href="/main/3/1" class="active">Sub Page 1</a>
                      </li>
                      <li>
                        <a href="/main/3/2">Sub Page 2</a>
                      </li>
                    </ul>
                  </li>
                </ul>
              </li>
              <li>
                <a href="/contact">Contact</a>
              </li>
            </ul>
          </li>
        </ul>
        Zuletzt geändert von h3ll; 12.01.2016, 20:47.

        Kommentar

        Lädt...
        X