从多维数组生成导航

时间:2010-05-20 20:47:26

标签: php iterator navigation

问题:如何生成导航,允许从多维数组中将不同的类应用于不同的子项?

在我需要多级导航之前,我就是这样做的:

Home 
Pics 
About

并通过调用nav()生成:

function nav(){       
    $links = array(
        "Home" => "home.php",
        "Pics" => "pics.php",
        "About" => "about.php"
    );

    $base = basename($_SERVER['PHP_SELF']);

    foreach($nav as $k => $v){
        echo buildLinks($k, $v, $base);
    }
}

这是buildLinks():

function buildLinks($name, $page, $selected){
    if($selected == $page){
       $theLink = "<li class=\"selected\"><a href=\"$page\">$name</a></li>\n";
    } else {
       $thelink = "<li><a href=\"$page\">$name</a></li>\n";
    }

    return $thelink;
}

我的问题,再次:

我如何实现以下导航(并注意到可见的子导航元素仅在该特定页面上出现时):

Home
    something1
    something2 
Pics 
About

和...

Home
Pics
    people
    places 
About

我尝试了什么

从它看来,似乎SPL中的某些迭代器很适合这个,但我不知道如何处理它。我玩过RecursiveIteratorIterator,但我不知道如何将不同的样式仅应用于子菜单项,以及如果只在正确的页面上显示这些项目。

我构建了这个数组以进行测试但不知道如何单独使用子菜单项:

$nav = array(
array(
"Home" => "home.php",
"submenu1" => array(
    "something1"=>"something1.php",
    "something2" => "something2.php")
),
array("Pics" => "pics.php"),
array("About" => "about.php")
);

以下内容将按顺序打印出来,但我如何申请,比如将一个类别名称应用于子菜单项目,或者仅在该人员开启时显示它们,例如“主页”页面?

$iterator = new RecursiveIteratorIterator(new RecursiveArrayIterator($nav));

foreach($iterator as $key=>$value) {
    echo $key.' -- '.$value.'<br />';
}

这让我:

Home
something1
something2
Pics
About

但我无法将类应用于这些子项,也无法只是有条件地显示它们,因为我看不到如何仅仅针对这些元素。

7 个答案:

答案 0 :(得分:3)

不要重新发明轮子,使用Zend_Navigation,你会很高兴。

答案 1 :(得分:1)

似乎您可能希望以更面向对象的方式执行此操作。 如果没有,似乎你应该至少定义一个有意义的算法,现在你只是盲目地猜测。相反,DEFINE。

例如:

我将导航定义为基于php散列的树。导航项将具有以下内容:

A)如果有顶级链接,则数组哈希将包含标记为“navigation leaf”的项(子数组)

b)导航Leaf将包含标记为“显示值”,“链接值”和“alt值”的元素。这些项目将用于生成锚标记。

c)如果元素具有子菜单,除了包含“导航叶”之外,还将存在“子导航”元素。如果子导航元素具有可显示的导航项,则它将具有“导航叶”。

然后,您可以编写将根据您选择的定义显示导航的功能/方法。

答案 2 :(得分:1)

我会做的是这样的事情:

class MenuItem {
    protected $active = false;
    protected $children = array();
    protected $name = '';
    protected $link = '';

    public function __construct($name, $link, $active) {}

    public function __toString() {
        //render this item
        $out = ''; #render here
        if (!$this->isActive()) { 
            return $out;
        }
        $out .= '<ul>';
        foreach ($this->children as $child) {
            $out .= (string) $child;
        }
        $out .= '</ul>';
        return $out;
    }

    public function isActive() {
        if ($this->active) {
            return true;
        }
        foreach ($this->children as $child) {
            if ($child->isActive()) {
                return true;
            }
        }
        return false;
    }
 }

然后,您拥有的只是数组中的根菜单项的集合...要构建菜单,您只需:

$rootItems = array($item1, $item2);
$out = '<ul>';
foreach ($rootItems as $item) {
    $out .= (string) $item;
}
$out .= '</ul>';

我将保留构造对象,向用户添加子等等的语义......

答案 3 :(得分:1)

如何以下一种方式重写导航功能:

function nav($links, $level){       
    foreach($links as $k => $v) {
        if (is_array($v)) {
            nav($v, $level + 1)
        } else {
            echo buildLinks($k, $v, $base);
        }
    }
}

而不是称之为:

$links = array(
array(
"Home" => "home.php",
"submenu1" => array(
    "something1"=>"something1.php",
    "something2" => "something2.php")
),
array("Pics" => "pics.php"),
array("About" => "about.php")
);
nav($links, 0);

答案 4 :(得分:1)

最简单的方法,恕我直言,只是进行递归调用,并使用导航的树结构描述(即嵌套数组)。未经测试的示例代码:

<?php
$links = array(
    "Home" => array("home.php", array(
        "something1"=> array("something1.php", array()),
        "hello"=> array("hello.php", array(
            "world" => array("world.php", array()),
            "bar" => array("bar.php", array()),
        )),
    )),
    "Pics" => array("pics.php", array(
        "people"=>"people.php",
        "places" => "places.php",
    )),
    "About" => array("about.php", array()), // example no subitems
);

// use the following $path variable to indicate the current navigational position
$path = array(); // expand nothing
$path = array('Home'); // expand Home
$path = array('Home', 'hello'); // also expand hello in Home

// map indent levels to classes
$classes = array(
    'item',
    'subitem',
    'subsubitem',
);


// recursive function to build navigation list
function buildNav($links, $path, $classes)
{
    // selected page at current level
    // NOTE: array_shift returns NULL if $path is empty.
    // it also alters the array itself
    $selected = array_shift($path);
    $class = array_shift($classes);

    echo "<ul>\n";

    foreach($links as $name => $link)
    {
        list($href, $sublinks) = $link;
        if ($name == $selected)
        {
            echo "<li class=\"selected $class\"><a href=\"$href\">$name</a>\n";
            // recursively show subitems
            // NOTE: path starts now with the selected subitem
            buildNav($sublinks, $path, $classes);
            echo "</li>\n";
        }
        else
        {
            echo "<li><a href=\"$href\" class=\"$class\">$name</a></li>\n";
        }
    }

    echo "<ul>\n";
}

// actually build the navigation
buildNav($links, $path, $classes);
?>

答案 5 :(得分:1)

你使用RecursiveIteratorIterator走在正确的轨道上。它基本上使递归迭代器变平。这是正确的方法:

$nav = array(
    array(
    "Home" => "home.php",
    "submenu1" => array(
        "something1"=>"something1.php",
        "something2" => "something2.php")
    ),
    array("Pics" => "pics.php"),
    array("About" => "about.php"),
);

$it = new RecursiveIteratorIterator(
    new RecursiveArrayIterator($nav),
    RecursiveIteratorIterator::SELF_FIRST
);

foreach ($it as $k => $v) {
    if ($it->getDepth() == 0)
        continue;
    echo str_repeat("    ", $it->getDepth() - 1) .
        "$k => $v\n";
}

给出

Home => home.php
submenu1 => Array
    something1 => something1.php
    something2 => something2.php
Pics => pics.php
About => about.php

答案 6 :(得分:1)

@catchmeifyoutry

谢谢,你救了我的生命。

我稍微改变了你的功能以适应我的使用,这就出现了:

$html['navi'] = array(
    "Home"          => "/home/",
    "DJs & Shows"   => "/djs-shows/",
    "Playlists"     => "/playlists/",
    "Newsbeat"      => "/newsbeat/",
    "Reviews"       => "/reviews/",
    "TV"            => "/tv/",
    "Contact"       => "/contact/",
    "Test"          => array("/test/",
        array("Submenu 1" => "/test/link1",
            "Submenu 2" => "/test/link2",
            "Submenu 3" => "/test/link3",
            "Submenu 4" => "/test/link4",
            "Submenu 5" => "/test/link5",
            "Submenu 6" => "/test/link6"
        )
    )
);

$classes = array(
    'first-level',
    'second-level',
    'third-level',
);

function siteNavi($links, $classes) {
    // The best way for MultiArray navigation (LOVE IT!)
    // Array Shift selects first element and removes it from array
    $class = array_shift($classes);

    echo "<ul class=\"$class\">\n";

    foreach($links as $name => $link) {

        if (is_array($link) AND $class != "") {

            list($link, $sublinks) = $link;
            if ($_GET['site'] == basename($link)) { $selected = ' class="current"'; } else { $selected = ""; }

            echo "<li{$selected}><a href=\"{$link}\">{$name}</a>\n";
            // recursively show subitems
            // NOTE: path starts now with the selected subitem
            siteNavi($sublinks, $classes);
            echo "</li>\n";

        } else {

            if ($_GET['site'] == basename($link)) { $selected = ' class="current"'; } else { $selected = ""; }

            echo "<li{$selected}><a href=\"{$link}\" >{$name}</a></li>\n";
        }
    }

    echo "</ul>\n";
}

非常感谢!

我想知道这种代码对页面速度有多大影响。几微秒毫秒:D