Создать навигацию из многомерного массива - PullRequest
7 голосов
/ 21 мая 2010

Вопрос: Как сгенерировать навигацию, позволяющую применять разные классы к разным подпунктам из многомерного массива?

Вот как я это делал до того, как мне понадобилась многоуровневая навигация:

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, но я не уверен, как применить другой стиль только к элементам подменю, а также как показать эти элементы, только если вы находитесь на правильной странице.

Я создал этот массив для тестирования, но не знаю, как работать с элементами подменю1 индивидуально:

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

Следующее будет распечатывать лот по порядку, но как мне применить, скажем, имя класса для пунктов подменю1 или показывать их только тогда, когда человек находится, скажем, на «Домашней» странице?

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

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

И это меня достает:

Home
something1
something2
Pics
About

Но у меня нет способа применить классы к этим подэлементам и нет способа отображать их только условно, потому что я не вижу, как нацеливать только эти элементы.

Ответы [ 7 ]

3 голосов
/ 21 мая 2010

Не изобретайте велосипед, используйте Zend_Navigation , и вы будете счастливы.

1 голос
/ 25 ноября 2012

@ catchmeifyoutry

Спасибо, ты спас мне жизнь, LoL.

Я немного изменил вашу функцию, чтобы адаптировать ее к своему использованию, и это получилось:

$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

1 голос
/ 21 мая 2010

Вы были на правильном пути с 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
1 голос
/ 21 мая 2010

Самый простой способ, IMHO, это просто сделать рекурсивный вызов и использовать древовидное описание вашей навигации (то есть вложенных массивов). Пример непроверенного кода:

<?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);
?>
1 голос
/ 21 мая 2010

А как насчет переписать функцию навигации следующим образом:

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);
1 голос
/ 21 мая 2010

Что бы я сделал, это что-то вроде этого:

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>';

Я оставлю семантику конструирования объекта, добавления потомков и т. Д. Пользователю ...

1 голос
/ 21 мая 2010

Кажется, вы можете сделать это более объектно-ориентированным способом. Если нет, кажется, что вы должны хотя бы определить алгоритм, который имеет смысл, сейчас вы просто слепо угадываете. Вместо этого, DEFINE.

Например:

Я определяю свою навигацию как дерево, основанное на хэше php. Элемент навигации будет иметь следующее:

A) если есть ссылка верхнего уровня, хэш массива будет содержать элемент (подмассив) с пометкой «лист навигации»

b) Лист навигации будет содержать элементы с метками «Отображаемое значение», «Значение ссылки» и «Альтернативное значение». Эти элементы будут использоваться для создания тега привязки.

c) если элемент имеет подменю, в дополнение к содержанию «навигационного листа», будет присутствовать элемент «субнавигация». Поднавигательный элемент будет иметь «Лист навигации», если он имеет отображаемый элемент навигации.

Затем вы можете написать функции / методы, которые будут отображать вашу навигацию на основе выбранного вами определения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...