В первой версии XSLT самой слабой, пожалуй, остаётся работа со строками.
Наиболее часто проблемы из-за этого возникают с генерацией URL’ов. Допустим, мы имеем страницу с адресом:
http://example.com/index.xml?section=catalog&cat-id=10&item-id=1404&item-unique-key=809124
Нам нужно, скажем, сгенерировать три ссылки: на главную страницу категории (cat-id), главную страницу (section) и независимую от категории страницу итема (item-id, item-unique-key).
Способов много, я по-началу даже использовал substring, но потом ради разнообразия включил моск и набросал небольшой шаблончик. Открытием для хоть сколько-то опытных разработчиков он не будет, так что будем считать это очередным self-note.
В первую очередь нам нужно надыбать xml с параметрами запроса. В идеале что-то вроде:
<state name="request">
<param name="section">catalog</param>
<param name="cat-id">10</param>
<param name="item-id">1404</param>
<param name="item-unique-key">809124</param>
</state>
Готовый вариант стилей будет выглядеть вот так:
<xsl:variable name="request" select="/page/state[@name='request']"/>
<xsl:template match="/">
<html>
<head>
<title>Custom page name</title>
</head>
<body>
<div id="place-holder">
<ul>
<li>
<a>
<xsl:attribute name="href">
<xsl:apply-templates select="$request">
<xsl:with-param name="exclude-params" select="'item-id|item-unique-key'"/>
</xsl:apply-templates>
</xsl:attribute>
Ссылка на общую страницу категории
</a>
</li>
<li>
<a>
<xsl:attribute name="href">
<xsl:apply-templates select="$request">
<xsl:with-param name="exclude-params" select="'cat-id'"/>
</xsl:apply-templates>
</xsl:attribute>
Ссылка на страницу итема, безотносительно категории
</a>
</li>
<li>
<a>
<xsl:attribute name="href">
<xsl:apply-templates select="$request">
<xsl:with-param name="exclude-params" select="'cat-id|item-id|item-unique-key'"/>
</xsl:apply-templates>
</xsl:attribute>
Ссылка на главную страницу раздела
</a>
</li>
</ul>
</div>
</body>
</html>
</xsl:template>
<xsl:template match="state">
<xsl:param name="exclude-params"/>
<xsl:text>http://example.com/index.xml</xsl:text>
<xsl:apply-templates select="param[not(contains($exclude-params,@name))]"/>
</xsl:template>
<xsl:template match="param[position()=1]">
<xsl:text>?</xsl:text>
<xsl:value-of select="@name"/>
<xsl:text>=</xsl:text>
<xsl:value-of select="."/>
</xsl:template>
<xsl:template match="param">
<xsl:text>&</xsl:text>
<xsl:value-of select="@name"/>
<xsl:text>=</xsl:text>
<xsl:value-of select="."/>
</xsl:template>
Разберём чуть подробнее:
<xsl:attribute name="href">
<xsl:apply-templates select="$request" name="create-url">
<xsl:with-param name="exclude-params" select="'cat-id|item-id|item-unique-key'"/>
</xsl:apply-templates>
</xsl:attribute>
Здесь мы запускаем шаблон для генерации URL’а, передавая ему, какие параметры не хотим видеть в создаваемом запросе. Если этих параметров в текущем URL’e нет, ничего страшного не случится.
Ну и, собственно, единственная соль:
<xsl:template match="state">
<xsl:param name="exclude-params"/>
<xsl:text>http://example.com/index.xml</xsl:text>
<xsl:apply-templates select="param[not(contains($exclude-params,@name))]"/>
</xsl:template>
Не матчим те параметры, которые мы передали как исключаемые. Ну и собственно генерируем URL:
<xsl:template match="param">
<xsl:text>&</xsl:text>
<xsl:value-of select="@name"/>
<xsl:text>=</xsl:text>
<xsl:value-of select="."/>
</xsl:template>
Исключительно простая штука, которая может сэкономить начинающим кучу времени и нервов. Правда нужно еще xml получать правильный, но про пинание программеров будет в следующий раз :)
Хороший стиль и красиво, особенно для 1.0.
Думаю с 2.0 то же можно написать в пару XPath :) Если в ближайшее время доживу до блога, жди 2.0 ответ :)
Мне стиль не понравился =(
* строки — это не стихия xsl, зачем писать ‘item-id|item-unique-key’ строкой если есть xpath
* следующий кусок раскопипащен:
<xsl:value-of select=”@name”/>
<xsl:text>=</xsl:text>
<xsl:value-of select=”.”/>
* почему копипаст это плохо становится ясно при добавлении str:encode-uri() к выводу значений
veged, честно говоря, ни по одному из пунктов толком тебя не понял.
* про xpath не понимаю сходу разницу. На мой взгляд, обычной строкой лучше читается в коде. Или речь о фильтрации прямо в select’е у apply?
Про str:encode-uri проблему не понял. Не использовал почти.
Сначала подумал и переписал шаблоны (оба)
match="param"так (DRY):
<xsl:template match="param" mode="value">
<xsl:value-of select="@name"/>=<xsl:value-of select="."/>
</xsl:template>
<xsl:template match="param[position()=1]">
<xsl:text>?</xsl:text>
<xsl:apply-templates select="." mode="value"/>
</xsl:template>
<xsl:template match="param[position()>1]">
<xsl:text>&</xsl:text>
<xsl:apply-templates select="." mode="value"/>
</xsl:template>
Потом подумал, что я бы написал так:
<xsl:key name="href" match="/page/state" use="@name"/>
<xsl:template match="/">
<html>
<head>
<title>Custom page name</title>
</head>
<body>
<div id="place-holder">
<p>Текущий URL: http://example.com/index.xml?section=catalog&cat-id=10&item-id=1404&item-unique-key=809124</p>
<ul>
<li>
<a>
<xsl:attribute name="href">
<xsl:apply-templates select="key('href','request')">
<xsl:with-param name="params">
<xsl:apply-templates select="key('href','request')/param[not(contains('item-id|item-unique-key',@name))]" mode="value"/> </xsl:with-param>
</xsl:apply-templates>
</xsl:attribute>
Ссылка на общую страницу категории
</a>
</li>
<li>
<a>
<xsl:attribute name="href">
<xsl:apply-templates select="key('href','request')">
<xsl:with-param name="params">
<xsl:apply-templates select="key('href','request')/param[@name='cat-id']" mode="value"/>
</xsl:with-param>
</xsl:apply-templates>
</xsl:attribute>
Ссылка на страницу итема, безотносительно категории
</a>
</li>
</ul>
</div>
</body>
</html>
</xsl:template>
<xsl:template match="state">
<xsl:param name="params" select="''"/>
<xsl:text>http://example.com/index.xml</xsl:text><xsl:if test="string-length($params)">?<xsl:value-of select="$params"/></xsl:if>
</xsl:template>
<xsl:template match="param" mode="value">
<xsl:if test="position()>1"><xsl:text>&</xsl:text></xsl:if><xsl:value-of select="@name"/>=<xsl:value-of select="."/>
</xsl:template>
(поправил комментарий, было не понятно включен HTML или нет)
Вполне резонно. Но мне не нравится читаемость такого варианта.
Согласен. Это плата за гибкость
читаемость прежде всего: никто не увидит гибкости в непонятном коде.