Практический XSLT. Использование в качестве шаблонизатора. Часть 2

В предыдущей статье мы разобрали основные аспекты построения шаблона с помощью XSLT. Однако, для полноценного шаблона нужно не только выводить меню сайта, но также и текстовый материал документа.

Вывод HTML-контента из XML-документа


Ранее, мы определили, что все содержание разделов в XML-документе публикуется в блоке <content>. При этом, каждый модуль содержимого помещается в узел <item id="1" container="1" sorting="2" type="html" method="html" title="Новости">.

Для простоты предположим что пока у нас нет на сайте никакого динамического наполнения: форумов, новостей и д.р. Соответственно, нам на данном этапе достаточно выбрать элементы item у которых указан тип html и раскидать их в шаблоне в соответствии с тем, к какому блоку они относятся (указано в атрибуте container).

Добавим в шаблон xsl/template.xsl следующий код:
<!-- Обработка HTML-методов/текстов -->
<xsl:template match="item" mode="html">
<h1><xsl:value-of select="@title"/></h1>
<xsl:value-of select="." disable-output-escaping="yes" />
</xsl:template>
<!-- /Обработка HTML-методов/текстов -->


Мы добавили шаблон для элементов item. В соответствии с ним мы будем для каждого текстового блока выводить его название (атрибут title) и сам текст. Для вывода текста мы воспользуемся инструкцией <xsl:value-of select="." disable-output-escaping="yes" />, в которой мы выбираем текущий элемент (.) и с помощью параметра-атрибута disable-output-escaping мы отключаем "маскирование" входного кода, т.е. текст будет выводиться "как есть". Это нам необходимо для корректного вывода тегов в коде форматирования текста.

Однако, как правило, наполнение сайта делают люди, которых мало волнует соответствие форматирования текста стандарту XHTML. В случае с использованием XSLT любой незакрытый тэг (тот же <br>) будет вызывать ошибку парсера. Я долгое время пытался придумать разного рода анализаторы кода для чистки вводимого контента (тех-же новостей) на сайт, пока Денис Креминский не подсказал мне одно простое, но весьма эффективное решение: поместить весь опасный код в CDATA.

Секция CDATA используется для того, чтобы обозначить части документа, которые не должны восприниматься как разметка. Секция CDATA начинается со строки '<![CDATA[' и заканчивается строкой ']]>'. Внутри самой секции не должна присутствовать строка ']]>'.


Другими словами, все что в CDATA XSL-парсером не анализируется и передается в конечный документ "как есть".

Теперь, нам нужно указать где нам нужно выводить те или иные модули. Для этого, откроем xsl/my_template/layout.xsl и найдем в где у нас начинается разбивка на блоки.

Предположим, что у вас в изначальном XHTML-шаблоне была следующая таблица:
<table id="content-table">
<tr>
<td id="content-left">
<!-- Основной блок текста -->
</td>
<td id="content-right">
<!-- Дополнительный блок текста -->
</td>
</tr>
</table>


В ней описывается простой двух-колоночный макет. Вырежем данный код (всю таблицу) и поместим ее в файл xsl/template.xsl в новый шаблон:
<!-- Описание блока содержимого для данного стиля страницы -->
<xsl:template match="content">
<table id="content-table">
<tr>
<td id="content-left">
<!-- Основной блок текста -->
</td>
<td id="content-right">
<!-- Дополнительный блок текста -->
</td>
</tr>
</table>
</xsl:template>
<!-- / Описание блока содержимого для данного стиля страницы -->


Мы создали шаблон для блока content. Соответственно, в основном файле xsl/my_template/layout.xsl нам нужно указать, где он должен вызываться. Для этого вставьте в на место вырезанной таблицы следующий код:
<!-- таблица модулей -->
<xsl:apply-templates select="content"/>
<!-- /таблица модулей -->


Осталось только добавить в xsl/template.xsl инструкции для вывода нужных нам блоков:
<!-- Описание блока содержимого для данного стиля страницы -->
<xsl:template match="content">
<table id="content-table">
<tr>
<td id="content-left">
<!-- Основной блок текста -->
<xsl:apply-templates select="item[@container = 1]" mode="html"/>
</td>
<td id="content-right">
<!-- Дополнительный блок текста -->
<xsl:apply-templates select="item[@container = 2]" mode="html"/>
</td>
</tr>
</table>
</xsl:template>
<!-- / Описание блока содержимого для данного стиля страницы -->


В результате, из нашего XML-документа основное наполнение страницы будет аккуратно разложено в соответствующие блоки шаблона.

Создание под-меню


Разберемся теперь более подробно со структурой нашего сайта.

В нашем XML-документе в блоке <sections> выводиться все дерево-структура сайта. При этом, для каждого элемента (раздела сайта) у нас предусмотрен атрибут section который задает тип раздела. Однако, наш шаблон для меню навигации пока никак это не учитывает.

Откроем xsl/navigation.xsl и доработаем немного шаблон <xsl:template match="sections" mode="global_menu">:
<!-- Глобальное меню навигации -->
<xsl:template match="sections" mode="global_menu">
<div id="menu-main">
<ul>
<xsl:apply-templates select="item[@section=1]" mode="global_menu"/>
</ul>
</div>
</xsl:template>
<!-- /Глобальное меню навигации -->


Мы указали, что обрабатывать будем только разделы, у которых указан тип section=1.

Далее, попробуем избавиться от конструкции <xsl:when test="descendant-or-self::*/@id = /node()/@id"> (с помощью нее мы определяли является ли текущим обрабатываемый пункт меню). Ничего плохого в этой конструкции нет, но мы можем ее упростить и сделать более универсально (может кому-то неудобно указывать номер текущего раздела в корневом узле и постоянно его проверять): попробуем передавать id текущего раздела в виде переменной.

Допустим, блок <sections> во входном XML-документе формируется отдельным модулем CMS и этот модуль указывает номер текущего раздела в корневом узле блока <sections>: <sections hit_id="2">. Таким образом, в шаблоне для элемента sections перед вызовом обработчиков для item нам нужно им как-то передать номер текущего раздела. Для этого воспользуемся переменными. Переделаем немного строку вызова обработчиков для item:
<xsl:apply-templates select="item[@section=1]" mode="global_menu">
<xsl:with-param name="cur"><xsl:value-of select="@hit_id"/></xsl:with-param>
</xsl:apply-templates>


Мы поместили внутрь вызова <xsl:apply-templates/> (сделав из него парный элемент) инструкцию <xsl:with-param name="cur">, которая создает параметр-переменную cur в контексте текущего вызова.

Теперь, упростим немного шаблон <xsl:template match="item" mode="global_menu">:
<!-- если Текущий раздел -->
<xsl:when test="descendant-or-self::*/@id = $cur">
<a><xsl:value-of select="title"/></a>
</xsl:when>


В проверку мы вставили переменную $cur. Некоторые, наверное спросят: а зачем нам descendant-or-self::*/@id, если можно сравнивать просто с @id, т.е. просто идентификатором текущего раздела? Дело в том, что если у раздела есть подразделы и мы попадаем на них, то нам нужно как-то отметить родительский раздел. Конструкция descendant-or-self::*/@id указывает совпадение атрибута id для узла или любого из его потомков.

Однако, нам нужно предусмотреть два варианта:

  • когда мы находимся в самом разделе - ссылка на раздел не нужна;
  • когда мы находимся в подразделе - ссылку на раздел делаем.



Рекомендуем почитать

 

Добавить комментарий


Ваше имя:


Комментарий:


Введите: Картинка