Pull to refresh

Постраничная навигация в XSLT

XSLT *
Sandbox
Здравствуйте уважаемые Хабровчане. Недавно встретилась задача вывода постраничной навигации с помощью технологии XSLT. После безрезультатных поисков по интернету типовых решений, было принято решение изобрести свой велосипед.

Дано: Номер текущей страницы получаемой из GET запроса. XML файл, состоящий из корневого элемента, в котором множество вложенных однотипных элементов. Для краткости сократим файл до вот такого вида:

Файл данных nav.xml
<?xml version="1.0" encoding="UTF-8"?>
<root>
	<item id="1"/><item id="2"/><item id="3"/><item id="4"/><item id="5"/><item id="6"/><item id="7"/><item id="8"/><item id="9"/><item id="10"/><item id="11"/><item id="12"/><item id="13"/><item id="14"/><item id="15"/><item id="16"/><item id="17"/><item id="18"/><item id="19"/><item id="20"/><item id="21"/><item id="22"/><item id="23"/><item id="24"/><item id="25"/><item id="26"/><item id="27"/><item id="28"/><item id="29"/><item id="30"/><item id="31"/><item id="32"/><item id="33"/>
</root>

Надо: Обеспечить постраничную навигацию. Если страница не первая выводилось назад и в начало. Если страница не последняя выводилось далее и в конец. Число элементов на странице, число страниц до и после текущей страницы можно было изменять.

После нескольких часов работы получилось следующее:

Файл преобразования nav.xsl
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

	<!-- Настройка вывода -->
	<xsl:output method="html" encoding="utf-8" omit-xml-declaration="yes" indent="no" doctype-system="about:legacy-compat"/>
	
	<!-- Номер текущей страницы -->
	<xsl:param name="page" select="1"/>
	
	<!-- Основное преобразование -->
	<xsl:template match="/">
		<!-- Число элементов на странице -->
		<xsl:variable name="pageItems" select="5"/>
	
		<!-- Вычисляем текущую страницу  -->
		<xsl:variable name="pageCurrent">
			<xsl:choose>
				<!-- Если текущая страница меньше одного -->
				<xsl:when test="number($page) < 1">
					<xsl:text>1</xsl:text>
				</xsl:when>
				<!-- Если текущая страница больше общего количества -->
				<xsl:when test="number($page) > ceiling(count(/root/*) div $pageItems)">
					<xsl:value-of select="ceiling(count(/root/*) div $pageItems)"/>
				</xsl:when>
				<xsl:otherwise>
					<xsl:value-of select="number($page)"/>
				</xsl:otherwise>
			</xsl:choose>
		</xsl:variable>

		<html lang="ru">
			<head>
				<title><xsl:value-of select="concat('Страница № ',$pageCurrent)"/></title>
				<!-- Немного стилей для красоты -->
				<style>
					#nav li{float:left;list-style:none;}
					#nav a{text-decoration:none;padding:4px;color:#333}
					#nav a.on, #nav a:hover{background:#bbb;}
				</style>
			</head>
			<body>
				<h1><xsl:value-of select="concat('Страница № ',$pageCurrent)"/></h1>
				<ul>
					<xsl:for-each select="/root/*[position() > ($pageCurrent * $pageItems - $pageItems) and position() <= ($pageCurrent * $pageItems)]">
						<li><xsl:value-of select="@id"/></li>
					</xsl:for-each>
				</ul>
				<hr/>
				<!-- Вызываем шаблон постраничной навигации -->
				<xsl:call-template name="pageNav">
					<xsl:with-param name="items" select="/root/*"/>
					<xsl:with-param name="pageCurrent" select="$page"/>
					<xsl:with-param name="pageItems" select="$pageItems"/>
					<xsl:with-param name="pageParty" select="4"/>
				</xsl:call-template>
			</body>
		</html>
	</xsl:template>

	<!-- Шаблон создания постраничной навигации -->
	<xsl:template name="pageNav">
		<!-- Элементы -->
		<xsl:param name="items"/>
		<!-- Текущая страница -->
		<xsl:param name="pageCurrent"/>
		<!-- Число элементов на странице -->
		<xsl:param name="pageItems"/>
		<!-- Число ссылок назад и вперед -->
		<xsl:param name="pageParty"/>

		<!-- Всего страниц -->
		<xsl:variable name="count" select="ceiling(count($items) div $pageItems)"/>

		<ul id="nav">
			<!-- В начало и назад -->
			<xsl:if test="$pageCurrent > 1">
				<li><a href="?page=1"><<</a></li>
				<li><a href="?page={$pageCurrent - 1}"><</a></li>
			</xsl:if>
			<!-- Центральные -->
			<xsl:for-each select="$items[(position() - 1) mod $pageItems = 0]">
				<xsl:if test="($pageCurrent - $pageParty) <= position() and ($pageCurrent + $pageParty) >= position()">
					<li><a href="?page={position()}">
						<xsl:if test="$pageCurrent=position()">
							<xsl:attribute name="class">on</xsl:attribute>
						</xsl:if>
						<xsl:value-of select="position()"/>
					</a></li>
				</xsl:if>
			</xsl:for-each>
			<!-- Следующая и в конец -->
			<xsl:if test="$pageCurrent < $count">
				<li><a href="?page={$pageCurrent + 1}">></a>	</li>
				<li><a href="?page={$count}">>></a></li>
			</xsl:if>
		</ul>
	</xsl:template>

</xsl:stylesheet>

Получилось длинновато, но не сложно. Таблица разделена на 2 части. В первой выводиться основная HTML разметка и контент в зависимости от страницы. Во второй непосредственно сама навигация.

Шаблон постраничной навигации вызывается по имени и принимает 4 параметра. Это выводимые элементы, номер текущей страницы, количество выводимых элементов на странице и число предыдущих и следующих страниц навигации.

Теперь напишем обработчик на PHP, хотя здесь может быть и любой другой язык.

Файл обработки nav.php
<?php
// Подготавливаем данные и таблицу стилей
$data = new DOMDocument('1.0', 'UTF-8');
$data->load('nav.xml');
$view = new DOMDocument('1.0', 'UTF-8');
$view->load('nav.xsl');

// Создаем XSLT процессор
$xsl = new XSLTProcessor();

// Импортируем таблицу стилей
$xsl->importStyleSheet($view);

// Если в запросе есть номер страницы, то передаем ее шаблону
if (isset($_GET['page'])) {
	$xsl->setParameter('', 'page', $_GET['page']);
}

// Преобразовываем данные и выводим на экран
echo $xsl->transformToXML($data);
?>

Тут все просто. Подгружаем данные и таблицу стилей. Создаем процессор XSLT. Если есть номер страницы, передаем его в таблицу. Выводим результат преобразования на экран.
Tags:
Hubs:
Total votes 9: ↑5 and ↓4 +1
Views 3.6K
Comments Comments 8