Навигация в 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>";
Andrey — 2012-11-18, 20:29

Спасибо вам человеческое!
Надо только не забыть сделать global $browse_map;
Чуствую что неткет мне разрушит мозг.

Max — 2013-01-25, 08:12

$browse_template у меня выводит, \$GLOBALS['browse_map'] - нет :(

Andrey — 2013-01-25, 08:18

Вот пример как делаю...

$browse_parentsub[prefix] = "<ul class='equipment-item-links'>";
$browse_parentsub[suffix] = "</ul>";
$browse_parentsub[unactive] = "<li><a href='%URL'>%NAME</a></li>";
$browse_parentsub[active] = "<li><a href='%URL'>%NAME</a></li>";
$browse_parentsub[active_link] = "<li><a href='%URL'>%NAME</a></li>";
$browse_parentsub[divider] = "";

$browse_sub[prefix] = "<div class='equipment cf'>";
$browse_sub[suffix] = "</div>";
$browse_sub[unactive] = "<div class='equipment-item cf'><div class='equipment-item-img'><img src='%Pic' alt=''></div><div class='equipment-item-data'><h3><a href='%URL'>%NAME</a></h3>\".s_browse_sub(\$data[\$i][Subdivision_ID],\$GLOBALS['browse_parentsub']).\"</div></div>";
</div>";
$browse_sub[0][divider] = "";

Вывожу: ".s_browse_sub(524,$browse_sub)." , 2-ой уровень не выводится :(

Andrey — 2013-01-25, 08:19

там ошибка, лишнее </div>"; - убрал, проблема не в этом, меню так и не выводится.

Друзья, забыл в статье добавить, что все это касается версии 4.72 Возможно, в пятерке есть какие-то свои дополнительные особенности и подводные камни...

Finar.

Max — 2013-01-29, 07:07

Пробую на версии 4.2, \$GLOBALS['browse_map'] не выводит. :'(

Вообще-то приблизительно похожий пример оказался рассмотрен в руководстве разработчика (на 168 стр. версии 4.2 и 189 стр. 4.7), только менее подробно. Попробуйте объявлять переменные так, как в тех примерах.

Finar.

Max — 2013-01-30, 07:22

Да уж, попробовал по инструкции (там в принципе аналогично), с моим шаблоном вывода не выводит, а если пишу шаблон $browse_template, то выводится. Также попробовал написать \$GLOBALS['browse_parentsub'], не получилось. Может где-то еще что-то нужно объявить ?

Давайте я приведу полный код, который работает у меня, а вы сравните:

// карта сайта в меню
$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.

Блог

Что с Мастерхостом? Когда заработает?!

Этот вопрос всё чаще задают в Интернете начиная примерно с 12:00 дня 2 марта. А всё потому, что он накрылся!

далее

Автоматизированная Система Управления Бэкапами

Автоматизированная Система Управления Бэкапами позволяет добиться полного контроля над резервными копиями сайтов внутри инфраструктуры веб-студии. Если вы поддерживаете десятки сайтов на разных хостингах, без подобной системы вы не можете быть на 100% уверены в том, что каждый из них был корректно зарезервирован прошлой ночью.

далее

WebSocket: интеграция с NetCat

Хотите добавить на сайт под управлением CMS NetCat поддержку технологии WebSocket? Обращайтесь к нам! Посетители сайта смогут получать мгновенные уведомления о событиях сайта без обращений к серверу и перезагрузок страниц. Превратите свой сайт в интерактивную площадку, работающую в реальном времени!

далее

NetCat: техническая поддержка и доработка сайтов

Мы работаем с CMS NetCat уже больше 10 лет. У нас большой опыт и ответственный подход к делу.

далее

Весь блог тут