Textpattern CMS support forum
You are not logged in. Register | Login | Help
- Topics: Active | Unanswered
How do I use if_different only above a certain number of matches?
I have a calendar of events that can have a very varying number of entries depending on whether filtered by organiser (=article category) or showing all organisations, as well as the time of year (lots of events in summer, fewer in winter).
I have used if_different to insert month headings between ul-lists of articles (using this clever method worked out by bloke and etc). That works great for long lists but looks really busy for shorter lists when some organisers have perhaps only a few events in a year. There’s then a month heading per event. In such cases – let’s say with fewer than 8 articles –, I’d rather do without the subheadings.
The only way I can think of doing this is to make a query to get the article id#s, count them, then output with or without subheadings.
To complicate matters, the original query is made using smd_query with the populate="article"
because it joins results from two different sets of articles and filters against dates stored in custom_fields). Once I have a comma-separated list of article-id#, I can feed them back into article_custom to output them in the desired format.
Two questions:
- Is there a cleverer way to do this, e.g. to cache the original query results and then re-output them differently without a renewed query? AFAIK using
populate="article"
in smd_query adds them to$thisarticle
. - Do I not need to worry about performance because Textpattern or mysql does some kind of caching in the background when making a repeat query?
TXP Builders – finely-crafted code, design and txp
Offline
Re: How do I use if_different only above a certain number of matches?
Following on from this idea of “querying the results of a previously executed query”, I’d also like to make a category filter based on the list of category1 values in the event list. Currently I duplicate the query but with a different output form.
TXP Builders – finely-crafted code, design and txp
Offline
Re: How do I use if_different only above a certain number of matches?
Your markup (ul-lists) is rich enough to allow etc_query
count list items and remove ‘short’ lists headings. But when do you need to remove them: if the total number of articles is less than 8, or the months list is shorter than 8, or..?
Offline
Re: How do I use if_different only above a certain number of matches?
I was thinking the total number of returned articles. I can get that by incrementing a counter in the listitem form.
Thanks for the idea with etc_query. You’re suggesting I save the output of the first query to a txp variable, and then, if my counter is less than my threshold value, that I use etc_query to collect all the relevant event-item html entries, skipping the headings. I guess then, the txp variable serves as the data
attribute, and that I would need an appropriate XPath filter to retrieve just the relevant markup.
Currently my markup with headings is:
<h4 class="event-month" id="march-2025">
March 2025
</h4>
<ul class="event-list plainlist">
<li>
<ul class="event-item prose flow">
<!-- the event details -->
</ul>
</li>
<li>
<ul class="event-item prose flow">
<!-- the event details -->
</ul>
</li>
...
</ul>
<h4 class="event-month" id="april-2025">
April 2025
</h4>
<ul class="event-list plainlist"> …
The desired markup without headings would mean retrieving each ul.event-item
and then rewrapping them with wraptag="ul" class="event-list plainlist" break="li"
, e.g.:
<ul class="event-list plainlist">
<li>
<ul class="event-item prose flow"> …
<ul class="event-item prose flow"> …
<ul class="event-item prose flow"> …
…
</li>
</ul>
TXP Builders – finely-crafted code, design and txp
Offline
Re: How do I use if_different only above a certain number of matches?
jakob wrote #339318:
Thanks for the idea with etc_query. You’re suggesting I save the output of the first query to a txp variable, and then, if my counter is less than my threshold value, that I use etc_query to collect all the relevant event-item html entries, skipping the headings. I guess then, the txp variable serves as the
data
attribute, and that I would need an appropriate XPath filter to retrieve just the relevant markup.
If you have etc_query
already installed, that’s probably the less resistance way. But it’s a large plugin (though it might replace smd_query
), so installing it just for this task looks like an overkill.
I was thinking the total number of returned articles. I can get that by incrementing a counter in the listitem form.
You seem to retrieve article ids with smd_query
and then passing the list to article_custom
? Then you can count the articles before calling article_custom
, and then use the appropriate form. For example, remove anything but commas from the list and calculate the remaining length
with, say, evaluate
.
Offline
Re: How do I use if_different only above a certain number of matches?
I’ve tried both ways for now.
Variant 1 with multiple, successive queries is as follows and uses smd_query to get a comma-separated list of the relevant article id numbers:
<txp:variable name="event_item_ids">
<txp:smd_query query='…' wraptag="" break="," populate="article">
<txp:article_id /><txp:variable name="event_count" add />
</txp:smd_query>
</txp:variable>
Using the incremental counter, I can then test for:
<txp:if_variable name="event_count" not>
<!-- message about no events -->
<txp:else />
<txp:evaluate query='<txp:variable name="event_count" /> > 8'>
<!-- list of events with month headings -->
<txp:article_custom form="event_item_by_month" id='<txp:variable name="event_item_ids" />' limit="0" />
<txp:else />
<!-- regular list of events -->
<txp:article_custom form="event_item" id='<txp:variable name="event_item_ids" />' limit="0" />
</txp:evaluate>
</txp:if_variable>
In practice, that doesn’t ‘seem’ particularly slow.
———
Variant 2 using etc_query is as follows: I first do the smd_query as before but with the full html output using the event_item_by_month
form and store all of that in a variable called events_by_month
. I also add <txp:variable name="event_count" add />
inside that form.
<txp:variable name="events_by_month">
<txp:smd_query query='…' wraptag="" break="" populate="article" form="event_item_by_month" />
</txp:variable>
Then I do the same event_count check for no output / the number of matches as above but outputting either the full html stored in the variable, or the stripped down html obtained using etc_query as follows:
<txp:if_variable name="event_count" not>
<!-- message about no events -->
<txp:else />
<txp:evaluate query='<txp:variable name="event_count" /> > 8'>
<!-- list of events with month headings -->
<txp:variable name="events_by_month" />
<txp:else />
<!-- regular list of events -->
<txp:etc_query data='<txp:variable name="events_by_month" />' markup="html" query="//ul[contains(@class, 'event-item')]" wraptag="ul" break="li" class="event-list plainlist" />
</txp:evaluate>
</txp:if_variable>
There’s an even more complex expression for an item that contains a class in this XPath cheatsheet (last item: Class check). FWIW, it also works with query="//ul//li//ul"
.
———
However, …
… I realise I have a thinking mistake when it comes to making the category filter in the sidebar. That should show all the possible categories with active events, but on pages where I am filtering by category, the original markup to filter from contains only the active category. So, I need to rethink that…
Possibilities:
- Variant with successive queries: maybe output all events regardless of category with smd_query, then filter by category in the
article_custom
instead, or … - Variant with etc_query: output all markup with smd_query, the filter the resulting html by category using etc_query, which means finding a more complex XPath query.
- Do two smd_queries, one for the category, one for all.
Given that 1. and 2. return an event count for all items, even when filtered by category, I’d need another way of testing for the number of matches. So I guess version 3 will be most likely. *sigh*. I suppose that’s not all that different to the typical configuration of using context-sensitive txp:article for the main column output and txp:article_custom for sidebar filters.
TXP Builders – finely-crafted code, design and txp
Offline
Re: How do I use if_different only above a certain number of matches?
I’d vote for:
- Variant 2: only one db query + fast dom parsing of a small fragment. You might even replace
etc_query
with some regex trim/replace. - Possibility 3: retrieves only necessary data, especially if you use
DISTINCT
for categories.
Offline
Re: How do I use if_different only above a certain number of matches?
Thank you. Well, I tried making a category filter for the pre-retrieved complete HTML output using etc_query and this XPath query:
//ul[contains(@class, 'event-item')][li[@class='event-item__ort' and text()='<txp:category />']]
That works quite well. However, I couldn’t work out a way to simply retrieve just the individual category values from the html output using XPath. I tried with:
//li[@class='event-item__ort']/text()
to get all the <li class="event-item__ort">…</li>
parts of the source, but the text is the category titles. But still, it seems I need to ‘resort’ to PHP to deduplicate the results and construct a filter table, this time using category titles. At this point, I feel like I’m jumping through hoops to replicate txp category filtering functionality…
… so, in the end I went for #3 using the following extra query:
<txp:smd_query query='SELECT DISTINCT Category1
FROM textpattern WHERE Status IN (4)
AND section = "kalender"
AND custom_24 >= CURDATE()
ORDER BY Category1 ASC'
break=",">{Category1}</txp:smd_query>
using your DISTINCT
tip (thank you!) which gives me a comma-separated list of category names of articles still in the calendar (custom_24 is the end_date). I can then use that for the categories
attribute of txp:category_list
to create a list of category filter links.
I learned something again today :-)
TXP Builders – finely-crafted code, design and txp
Offline
Re: How do I use if_different only above a certain number of matches?
Two css selectors :has()
and :nth-child()
can be combined to hide elements within a list according to the list’s size.
Something along the lines of:
.event-month {
display: none;
}
div:has(> .event-list:nth-child(6)) {
.event-month {
display: block;
}
}
Offline
Re: How do I use if_different only above a certain number of matches?
jakob wrote #339330:
it seems I need to ‘resort’ to PHP to deduplicate the results
Not necessary, valueless replace
removes duplicates (or is it a 4.9 thing?):
<txp:variable name="test" value="a,b,ab,b,a" breakby replace break="," output />
<!-- outputs 'a,b,ab' -->
But dom-parsing large chunks might be unnecessarily expensive.
giz wrote #339331:
Two css selectors
:has()
and:nth-child()
can be combined to hide elements within a list according to the list’s size.
CSS powers fascinate me, but can it also alter the html markup?
Offline
Re: How do I use if_different only above a certain number of matches?
giz wrote #339331:
Two css selectors
:has()
and:nth-child()
can be combined to hide elements within a list according to the list’s size.
Clever idea. Now why hadn’t I thought of that?! Obviously overthinking things. Thanks!
etc wrote #339332:
Not necessary, valueless
replace
removes duplicates (or is it a 4.9 thing?):
<txp:variable name="test" value="a,b,ab,b,a" breakby replace break="," output />...
I’m on the most recent beta, so that’s okay, but I didn’t know about the valueless replace
attribute. Another thing learned!
CSS powers fascinate me, but can it also alter the html markup?
No, it just hides the titles from view on all lists except those with more than 6 children.
TXP Builders – finely-crafted code, design and txp
Offline
Re: How do I use if_different only above a certain number of matches?
etc wrote #339332:
CSS powers fascinate me, but can it also alter the html markup?
It can in a limited way – but for the most part, sadly no.
Offline