Навигация в NetCat: неожиданные подводные камни
Система программирования навигации сайта в NetCat ммм... скажем, неинтуитивная. Есть в ней кое-какие весьма необычные и неочевидные особенности. Например, что дочерний шаблон навигации должен быть в коде расположен выше родительского. Но это еще общеизвестные цветочки, а сегодня рассмотрим пример задачи, которую архитектура NetCat переваривает совсем со скрипом. Заодно и разберемся, как работает обработка шаблонов навигации.
Итак, задача: создать такую Карту Сайта, в которой первый уровень имеет дизайн А, а все последующие до бесконечности - дизайн Б.
Казалось бы, задача довольно проста - для первого уровня сайта нужно создать первый шаблон навигации, в котором в цикле вызывать функцию генерирования навигации по второму шаблону. И расположить второй шаблон над первым. Попробуем это реализовать:
В макете дизайна вызываем функцию генерации разделов сайта: ".s_browse_sub(0,$browse_through)."
В шаблонах вывода навигации опишем массив $browse_through:
$browse_through['prefix'] = "<ul class='root-level'>"; $browse_through['unactive'] = "<li><a href=%URL>%NAME</a> МЕСТО_ВЫЗОВА_ВЛОЖЕННЫХ_РАЗДЕЛОВ </li>"; $browse_through['active'] = "<li><a href=%URL>%NAME</a> МЕСТО_ВЫЗОВА_ВЛОЖЕННЫХ_РАЗДЕЛОВ </li>"; $browse_through['suffix'] = "</ul>";
Создадим еще один массив, назовем его $browse_map, для генерации бесконечного древовидного меню. На сайте Netcat есть статья про навигацию (с кучей ошибок) возьмем оттуда код навигации для карты сайта:
$browse_map['prefix'] = "<ul>"; $browse_map['unactive'] = "<li><a href=%URL>%NAME</a>".s_browse_sub($data[$i][Subdivision_ID],$browse_template).""; $browse_map['active'] = "<li><a href=%URL>%NAME</a>".s_browse_sub($data[$i][Subdivision_ID],$browse_template).""; $browse_map['suffix'] = "</ul>";
Можете для теста вызвать этот массив нарпямую ".s_browse_sub(0,$browse_map)."
и убедиться, что код не заработает. И не только потому, что в примере пропущен закрывающий </li>
, но еще и потому, что шаблоны навигации в NetCat генерятся в самую первую очередь, еще до обработки самого html-макета. Таким образом, к моменту, когда мы вызовем функцию s_browse_sub, конструкция ".s_browse_sub($data[$i][Subdivision_ID],$browse_template)."
уже будет обработана! Естественно, в этот момент вместо номера раздела в элементе $data[$i][Subdivision_ID] будет пустота - ноль - в результате чего s_browse_sub сгенерит меню от корня сайта, а не нужного нам раздела. Очевидно, что данный код должен исполняться уже после вызова s_browse_sub, т.е. в самом шаблоне он должен быть однократно заэкранирован (о чем, кстати, и написано в документации). Не буду томить, правильный код вот такой:
// карта сайта $browse_map['prefix'] = "<ul>"; $browse_map['unactive'] = "<li><a href=%URL>%NAME</a>\".s_browse_sub(\$data[\$i][Subdivision_ID],\$browse_template).\"</li>"; $browse_map['active'] = "<li><a href=%URL>%NAME</a>\".s_browse_sub(\$data[\$i][Subdivision_ID],\$browse_template).\"</li>"; $browse_map['suffix'] = "</ul>";
Что такое в этом коде $browse_template? В $browse_template хранится этот же самый массив. То есть это как бы указание генерить подразделы по этому же шаблону, цикл.
ОК, разобрались, теперь переходим к самому ммм... неожиданно сложному. Мы успешно создали два массива с шаблонами, каждый их которых сам по себе работает, осталось казалось бы самое простое - вызвать второй массив из первого. Напомню, как это у нас сейчас выглядит (на примере одной строчки):
$browse_through['unactive'] = "<li><a href=%URL>%NAME</a> МЕСТО_ВЫЗОВА_ВЛОЖЕННЫХ_РАЗДЕЛОВ </li>";
Вместо МЕСТО_ВЫЗОВА_ВЛОЖЕННЫХ_РАЗДЕЛОВ очевидно нужно написать что-то вроде s_browse_sub($data[$i][Subdivision_ID],$browse_map)
, где $data[$i][Subdivision_ID] - id-шник текущего родительского раздела.
Попробуем:
$browse_through['unactive'] = "<li><a href=%URL>%NAME</a>".s_browse_sub($data[$i][Subdivision_ID],$browse_map)."</li>";
Не работает, по той же причине, что и в примере с картой сайта - в момент вызова функции из макета дизайна, тут уже сгенерировалась карта сайта от его корня.
Экранируем, пробуем снова:
$browse_through['unactive'] = "<li><a href=%URL>%NAME</a>\".s_browse_sub(\$data[\$i][Subdivision_ID],\$browse_map).\"</li>";
Не работает. Думаем и охаем и пробуем снова:
$browse_through['unactive'] = "<li><a href=%URL>%NAME</a>\".s_browse_sub(\$data[\$i][Subdivision_ID],\$browse_template).\"</li>";
О! Почему-то переменная $browse_template работает (см. ее описание в начале статьи), и в результате мы получаем карту сайта, сделанную целиком по шаблону browse_through (а не только первый уровень, как мы хотим). В чем же дело? Очевидно, в области видимости переменных. Внутри функции s_browse_sub массивы с шаблонами навигации не видны! (Если еще немного подумать, и это логично.s_browse_sub - это функция NetCat, а массивы создаются программистами. Разумно, что разработчики NetCat не могут знать наперед, в какие имена массивам придумают разработчики сайта. Другое дело, что можно было бы что-нибудь с этим придумать :)) Что же делать? Взять нужную нам переменную $browse_map из глобальной области видимости, причем сделать это "одной фразой" прямо внутри строкового элемента массива, вот так: $GLOBALS['browse_map']
Наконец, мы добрались до решения задачи:
$browse_through['unactive'] = "<li><a href=%URL>%NAME</a>\".s_browse_sub(\$data[\$i][Subdivision_ID],\$GLOBALS['browse_map']).\"</li>";
Друзья, забыл в статье добавить, что все это касается версии 4.72 Возможно, в пятерке есть какие-то свои дополнительные особенности и подводные камни...
Finar.
Вообще-то приблизительно похожий пример оказался рассмотрен в руководстве разработчика (на 168 стр. версии 4.2 и 189 стр. 4.7), только менее подробно. Попробуйте объявлять переменные так, как в тех примерах.
Finar.
Давайте я приведу полный код, который работает у меня, а вы сравните:
$browse_map['prefix'] = "<ul class='main-menu second-level'>";
$browse_map['unactive'] = "<li><a href=%URL>%NAME</a>\".s_browse_sub(\$data[\$i][Subdivision_ID],\$browse_template).\"</li>";
$browse_map['active'] = "<li><a href=%URL>%NAME</a>\".s_browse_sub(\$data[\$i][Subdivision_ID],\$browse_template).\"</li>";
$browse_map['suffix'] = "</ul>";
// вертикальное меню с разделителями
$browse_through['prefix'] = "<ul class='nav nav-pills nav-stacked'>";
$browse_through['unactive'] = "
\".(\$data[\$i][divider] == 1 ? \"<li class='nav-header'>%NAME\": \"<li><a href=%URL>%NAME</a>\" ).\"
\".s_browse_sub(\$data[\$i][Subdivision_ID],\$GLOBALS['browse_map'],\$ignore_check = 1).\"</li>";
$browse_through['active'] = "
\".(\$data[\$i][divider] == 1 ? \"<li class='nav-header'>%NAME\": \"<li class='active'><a href=%URL>%NAME</a>\" ).\"
\".s_browse_sub(\$data[\$i][Subdivision_ID],\$GLOBALS['browse_map'],\$ignore_check = 1).\"</li>";
$browse_through['suffix'] = "</ul>";
Finar.