Вывод данных в виде древовидных структур - задача достаточно типовая. Это может быть дерево сообщений в форумах и блогах, подобных этому, отображение карты сайта, различные многоуровневые рубрикаторы и т.п. Способов организации таких структур в базах данных достаточно много, материалов в Сети тоже (см. пордборку в конце этой заметки). Применительно к блогу рассмотрим самый простой (но не самый эффективный) способ, при котором в каждой записи хранится свой идентентификатор id и идентификатор родителя parentid. В виде XML ветка обсуждения в блоге имеет вид типа:
<item> <topic id="1" parentid="0" author="Иванов И.И." > Лучший шаблонизатор - PHP</topic> <topic id="9" parentid="8" author="Говорухо-Отрок С.П." > Smarty - вот шаблонизатор</topic> <topic id="13" parentid="9" author="Тынгылчав И.И." > Нет Smarty это дурь</topic> <topic id="14" parentid="13" author="Рабинович П.Е." > Нет. дурь - это не Smarty</topic> <topic id="15" parentid="9" author="Вышку Н.Н." > Кто про дурь говорит?</topic> .................................................................................... .................................................................................... .................................................................................... </item> |
Как приличные люди, мы должны организовать вывод этой ветки в виде семантически-грамотной верстки. Все сообщения в совокупности - ненумерованный список UL. Каждоее сообщение в отдельности - элемент ненумерованного списка LI. Каждое сообщение может иметь вложенный список сообщений - откликов.
<ul>
<li id="1"> <h3> Иванов И.И."</h3> Лучший шаблонизатор - PHP
<ul>
<li id="9"> <h3> Говорухо-Отрок С.П.</h3> Smarty - вот шаблонизатор
<ul>
<li id="13"> <h3> Тынгылчав И.И.</h3> Нет Smarty - это дурь</li>
<ul>
<li id="14"> <h3> Рабинович П.Е.</h3> Нет. дурь - это не Smarty</li>
....................................................................................
....................................................................................
</ul>
</ul>
</li>
</ul>
</li>
</ul> |
Шаблон для такого преобразования достаточно простой.
<xsl:template match="item">
<ul>
<xsl:apply-templates select="topic[@parentid=0]" />
</ul>
</xsl:template>
<xsl:template match="topic">
<xsl:variable name="id" select="@id"/>
<li id="{@id}"> <h3> <xsl:value-of select="@author"/> пишет:</h3>
<p> <xsl:apply-templates/> </p>
<ul>
<xsl:apply-templates select="../topic[@parentid=$id]"/>
</ul>
</li>
</xsl:template> |
Первый шаблон (match="item") зацепляет первое сообщение ветки обсуждения у которого нет родителя. Т.е. parentid=0. Далее запускается рекурсивная процедура (match="topic") которая выводит текущее сообщение и вытаскивает всех детей этой ветки. Т.е. сообщения являющиеся откликом непосредственно на текущее.
Как всегда не могу не сделать замечания относительно производительности. XSLT-преобразования ресурсоемкие. Запускать в них итерации не всегда разумно. Хотя в моем блоге формирование дерева производится не на стороне сервера, а на стороне клиента. За счет этого производительность не страдает. Но клиентское XSLT-преобразование - экзотика, допустимая только из любви к искусству. По крайней мере ближайшие годы.
Как обычно, все исходники можно найти в архиве xslt-examples.zip в директории ex6