Динамическое навигационное меню с использованием SSI Apache
Задача
Итак, есть статичный сайт, в котором существует динамическое навигационное меню, динамика которого заключается в том, что бы подсвечивать пункт из него соответствующий текущей странице.
Как сделать такое меню используя только SSI?
Решение более или менее зависит от реализации структуры самого сайта, варианты:
Один к одному - когда каждому пункту меню соответствует только одна страница;
Один ко многим - когда пункту меню соответствует несколько страниц с главной страницей;
Многие ко многим - когда пункту меню соответствует несколько страниц с главной страницей, и каждая страница соответсвует нескольким пунктам, например, если у нас многоуровневое меню;
Теперь каждый вариант в отдельности:
Один к одному
Самая простая в реализации задача. реализуется условиями:
Я специально буду использовать в примере разные переменные окружения, что бы показать несколько вариантов.
Как видно, относительную сложность вызывает только пункт "Главная" так как ссылка на эту страницу в регулярном выражении будет будет подходить для всех страниц. Для в переменных окружения $DOCUMENT_URI и $SCRIPT_NAME регулярное выражение и не нужно, так как в эти переменные не входит $QUERY_STRING как в $REQUEST_URI.
Все бы хорошо, но изменять такое меню несколько неудобно из-за нагромождения SSI директив. Поэтому предлагаю сделать для пункта меню шаблон и упростить определение списка.
Так в компоненте меню будет такой код:
А компонента шаблона (/ssi/navi.template.shtml) выглядит так:
Правда, есть одно "но" - регулярные выражения возвращают найденное значение в скобках только начиная с версии Apache 2.x, увы, для версии Apache 1.3 данное решение не рабочее. Для Apache 1.3 можно использовать такое решение:
А в компоненте шаблона:
Еще можно обратить внимание на то, что QUERY_STRING проверяется еще и как QUERY_STRING_UNESCAPED потому, что ... [почему?]
Один ко многим
Если пункт меню подразумевает не одну страницу, а группу страниц (раздел сайта), то добавляется еще одно свойство: Активный пункт, но со ссылкой на главную страницу группы (раздела). В этом случае тоже может быть несколько условий и вариантов решений:
Страницы сгруппированы по папкам, решения:
Вычислять раздел относительно $REQUEST_URI частичным совпедением, главная страница - полное совпадение;
В .htaccess папки раздела прописать соответствующую переменную окружения, главная страница - полное совпадение или дополнительная переменная окружения;
В каждой странице группы прописать соответствующую переменную окружения, главная страница - полное совпадение или дополнительная переменная окружения;
Страницы находятся в одной папке, но относятся к разным группам, решение:
Смотреть п.3 предыдущего варианта;
Страницы находятся, как повезет, решение:
Упорядочить страницы ;-). Смотреть п.2 и п.3 первого варианта;
Как видно, основных решений для группировки всего два:
Соответствие $REQUEST_URI и корневого URI раздела;
По Переменной окружения;
Для главной же страницы раздела:
Полное соответствие $REQUEST_URI без ($QUERY_STRING) и корневого URI раздела;
По Переменной окружения;
В соответствии с этим можно определить два алгоритма, алгоритм с использованием $REQUEST_URI:
Как видно, определение меню и вызов компоненты шаблона не отличается от предыдущего кода, но компонента несколько другая:
Как видно, для страниц группы (раздела) появился третий вид (выделенная ссылка), который выделяет соответствующий пункт, но оставляет ссылку на соответствующу главную страницу группы (раздела). Следует учесть, что данное решение работает только при условии того, что все группы у нас разбиты по папкам и главные их станицы, являются индексными файлами папки.
Выше мы рассмотрели вариант, когда страницы групп у нас упорядочены по папкам, но не всегда такой порядок присутствует на сайте, что, кстати, плохо. Следовательно, по REQUEST_URI можно найти только главную страницу группы, остальные придется определять переменными окружения:
Как видно, компонента вызова немного изменена, в частности добален еще один параметр, который будет для нас синонимом (alias) группы. На самом деле, в качестве синонима можно использовать как название группы, так и ссылку на главную страницу группы, но сайт у нас в беспорядке, возможны постоянные изменения названий и ссылок, посему опираться на эти параметры не стоит. Соответственно компонента шаблона:
И в каждой, не главной, странице группы прописываем дополнительную переменную в самом начале кода:
Вот так, причем данный алгоритм можно использовать и по упорядоченному сайту, при этом не прописывая соотвествие каждой страницы группе, а установив переменную PAGE_GROUP в .htaccess папки.
Опять же, для Apache 1.3 потребуется передавать не сложный параметр в компоненту, а простой, плюс еще две переменных устанавливать до вызова компоненты, как в предыдущем варианте. Однако, можно сократить количество кода, если alias группы будет основываться на её названии или на url главной страницы.
Многие ко многим
Вообще, этот вариант, мягко скажем, мало применим для статичных сайтов, ввиду сложности структуры последнего, но, все же, не исключаем и его.
Варианты решений в принципе, такие же, как и при связи "один ко многим". Причем если организация структуры у нас систематизирована, то есть группы страниц распределены по папкам, а подгруппы по папкам и в папках соответствующих групп, то и менять в компонентах нам ничего и не надо, только возможно условия вывода групп и подгрупп будут отличаться, что можно решить еще до вызова компоненты шаблона, например:
Как видно из примера, в вертикальном меню различия между группами и подгруппами в верстке достигнуты с помощью CSS стилей, а организация URL групп позволяет определять подчиненность.
Для смешанной структуры, когда полный бардак по разделам, можно организовать синонимы как в упорядоченных разделах, т.е.:
При этом в компоненте шаблона, GROUP_ALIAS с PAGE_GROUP мы сравниваем не один к одному, а через регулярное выражение:
Но я бы все-таки сделал бы реструктуризацию, до нормального состояния.
Важные грабли
Имейте ввиду, что mod_includeApache версий 1.3 и 2.x различен даже на уровне директив, так для версии 1.3 директива:
будет рабочей, то для 2.х - нет, так как пробел после символа # и директивой - запрещен, поэтому для версии 2.x (но не для 1.3) будет работать директива:
Причем для разных директив разные условия применения пробела между символом # и директивой, так, например, для echo пробел запрещен всегда. То же касается и mod_ssi для nginx, у него вообще принцип работы условий и регулярных выражений в них свой.