PHP-Scripte PHP-Tutorials PHP-Jobs und vieles mehr

PHP-Scripte PHP-Tutorials PHP-Jobs und vieles mehr (https://www.php-resource.de/forum/)
-   PHP Developer Forum (https://www.php-resource.de/forum/php-developer-forum/)
-   -   Problem mit mehrstufiger Navigation mit php (https://www.php-resource.de/forum/php-developer-forum/105436-problem-mit-mehrstufiger-navigation-mit-php.html)

Theryl 10-01-2016 05:47

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";
        }
      }
    }
  }
}
?>


mermshaus 11-01-2016 10:24

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).

fireweasel 12-01-2016 14:49

Verschachtelte Menüs, Nested Menus, Bäume und Trees, Walk, Traverse, ...
 
Zitat:

Zitat von Theryl (Beitrag 672124)
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. :p

--
[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.

h3ll 12-01-2016 21:44

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>



Alle Zeitangaben in WEZ +2. Es ist jetzt 19:40 Uhr.

Powered by vBulletin® Version 3.8.2 (Deutsch)
Copyright ©2000 - 2020, Jelsoft Enterprises Ltd.
Search Engine Friendly URLs by vBSEO 3.3.0
[c] ebiz-consult GmbH & Co. KG