Вопрос: Как я генерирую навигацию, допуская применение различных классов к различным подэлементам, от многомерного массива?
Вот то, как я делал его, прежде чем у меня была любая потребность в многоуровневой навигации:
Home
Pics
About
и был сгенерирован путем называния военно-морским ():
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;
}
Мой вопрос, снова:
как был бы я достигать следующего военно-морского (и заметьте, что видимые sub элементы навигации только присутствуют когда на той определенной странице):
Home
something1
something2
Pics
About
и...
Home
Pics
people
places
About
Что я попробовал
От рассмотрения его казалось бы, что некоторый итератор в SPL будет подходящим вариантом для этого, но я не уверен, как приблизиться к этому. Я играл вокруг с RecursiveIteratorIterator, но я не уверен, как применить другой стиль только к объектам подменю и также как только показать эти объекты, если Вы находитесь на корректной странице.
Я создал этот массив для тестирования с, но не знаю, как работать с submenu1 объектами индивидуально:
$nav = array(
array(
"Home" => "home.php",
"submenu1" => array(
"something1"=>"something1.php",
"something2" => "something2.php")
),
array("Pics" => "pics.php"),
array("About" => "about.php")
);
Следующее распечатает партию в порядке, но как я применяю, говорю имя класса submenu1 объектам или только показываю им, когда человек идет, скажем, страница "Home"?
$iterator = new RecursiveIteratorIterator(new RecursiveArrayIterator($nav));
foreach($iterator as $key=>$value) {
echo $key.' -- '.$value.'<br />';
}
И это получает меня:
Home
something1
something2
Pics
About
Но у меня нет способа применить классы к тем sub объектам и никакому способу только отобразить их условно, потому что я не вижу, как быть нацеленным просто на эти элементы.
Вы были на правильном пути с 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
Похоже, вы могли бы сделать это более объектно-ориентированным способом. В противном случае кажется, что вы должны хотя бы определить алгоритм, который имеет смысл, прямо сейчас вы просто слепо гадаете. Вместо этого DEFINE.
Например:
Я определяю свою навигацию как дерево на основе хэша php. Элемент навигации будет иметь следующее:
A) если есть ссылка верхнего уровня, хэш массива будет содержать элемент (подмассив) с меткой «лист навигации»
б) лист навигации будет содержать элементы с меткой « Отображаемое значение »,« значение ссылки »и« значение alt ». Эти элементы будут использоваться для создания тега привязки.
c) если у элемента есть подменю, помимо содержания «Листа навигации», будет присутствовать элемент «подменю». Элемент поднавигации будет иметь «Лист навигации», если у него есть отображаемый элемент навигации.
Затем вы можете написать функции / методы, которые будут отображать вашу навигацию на основе выбранного вами определения.
А как насчет перезаписи функции навигации следующим образом:
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);
Что я бы сделал, так это что-то в этом роде:
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>';
Я оставлю семантику построения объекта, добавления детей и т.д. пользователю...
ИМХО, самый простой способ - это просто сделать рекурсивный вызов и использовать древовидное описание вашей навигации (то есть вложенные массивы). Непроверенный пример кода:
<?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);
?>
Не изобретайте велосипед, используйте Zend_Navigation , и вы будете счастливы.