Go to main content

Textpattern CMS support forum

You are not logged in. Register | Login | Help

#1 2024-08-14 22:49:48

Bloke
Developer
From: Leeds, UK
Registered: 2006-01-29
Posts: 11,480
Website GitHub

Add info to nested category_lists

This is going to seem complicated on the surface, apologies.

Imagine I run an estate agency and I have a bunch of properties in cities around the globe. I have 2 category trees set up:

Bedrooms
  • 1-bed => One
  • 2-bed => Two
  • 3-bed => Three
Locations
  • Paris
  • London
  • Rome
  • New York
  • Los Angeles

Each article represents a property in the properties Section, and category1 = number of bedrooms, category2 = location. This isn’t the actual situation, btw, it’s just a simple analogy to the site I’m working on (because bedrooms would be a custom field of course in real life).

So my goal is to output an accordion style list of properties with a count of how many properties there are with that number of bedrooms at each location:

<h2 class="location-heading">London</h2>
<div class="accordion-container">
    <div class="accordion-hblock">
        <h3 class="accordion-header">Two-bedroom <span class="p_count">(1)</span></h3>
    </div>
    <ul class="properties accordion-open">
        <li><a rel="bookmark" href="/properties/quaint-bungalow">Quaint bungalow</a></li>
     </ul>
</div>

<div class="accordion-container">
    <div class="accordion-hblock">
        <h3 class="accordion-header">Three-bedroom <span class="p_count">(2)</span></h3>
    </div>
    <ul class="properties accordion-open">
        <li><a rel="bookmark" href="/properties/thames-view-apartment">Thames view apartment</a></li>
        <li><a rel="bookmark" href="/properties/kew-gardens-greenhouse">Kew Gardens greenhouse</a></li>
    </ul>
</div>
<div class="contact-info">Call our UK specialist, Stef on 0123456789.</div>

<h2 class="location-heading">Paris</h2>
<div class="accordion-container">
    <div class="accordion-hblock">
...
    </div>
</div>
<div class="contact-info">Call our France specialist, Stefanie on 987654321.</div>

I’ve got this working with some horrible hacky use of variables inside two <txp:category_list> tags to:

  1. Build a variable containing the <ul> block.
  2. Build a variable containing the accordion header <div> info.
  3. Set up variables to store the current category in each of the nested category lists.
  4. Output the above two variables in the correct sequence to construct the HTML, after each loop iteration.
  5. Tack on the contact info after all the properties in each location have been displayed (i.e. at the end of each location).

I’ve got the first 4 done, it’s the last item that I’m struggling with because I can’t find a way of only showing it after the last bedroom category has been displayed.

I can’t output it every time through the loop.
I can’t output it by comparing the name of the current bedroom category to the maximum (because someone might create more categories and that would require changing the code) and also, not all locations have a full set of properties with all bedrooms represented.
I can’t use <txp:if_last_category> because it doesn’t behave how I expect (perhaps due to the nested behaviour of the tags?). It only fires if articles with 5-bed are included, regardless of how many are at each location. It seems to know there are (currently) 5 categories so it only displays the div when one of the locations has a 5-bed property.

Here’s the current code anyway. I’ve documented it where appropriate. I can’t help thinking that there’s probably a neater way to do this with breakby and the spaceship <+>. This is a Txp 4.8.8 site but it could be pushed to 4.9.0-beta.1 if necessary.

<txp:hide>** Property list accordion **</txp:hide>
<category::list parent="location" sort="title desc" exclude="location" break="">
    <txp:variable name="n_location"><txp:category /></txp:variable>
    <txp:variable name="t_location"><txp:category title /></txp:variable>
    <txp:variable name="anchor"><txp:variable name="n_location" /></txp:variable>

    <txp:hide>** Build a list of article IDs at this location **</txp:hide>
    <txp:variable name="location_ids" escape="trim">
        <article::custom section="properties" status="live" limit="999" category='<txp:variable name="n_location" />' match="Category2" break=",">
            <txp:article_id />
        </article::custom>
    </txp:variable>

    <txp:variable name="p_count" value="0" />

    <txp:hide>** If there are some articles, loop over the bedrooms and display them in order **</txp:hide>
    <if::variable name="location_ids" not value="">
        <category::list parent="bedrooms" sort="name asc" exclude="bedrooms" children="2" break="">
            <if::different><h2 class="location-heading"><txp:variable name="t_location" /></h2></if::different>
            <txp:variable name="n_beds"><txp:category /></txp:variable>
            <if::variable name="anchor" not value=""><a id="<txp:variable name="anchor" />"></a><txp:variable name="anchor" value="" /></if::variable>
            <txp:variable name="group_block" value="" />

            <txp:hide>** Create a variable that contains the set of properties at this location, in bedroom order **</txp:hide>
            <txp:variable name="group_block" escape="trim">
            <txp:evaluate test="article_custom">
                <ul class="properties accordion-open">
                    <article::custom id='<txp:variable name="location_ids" />' limit="999" category='<txp:variable name="n_beds" />' match="Category1">
                        <txp:variable name="p_count" add />
                        <li><txp:permlink><txp:title /></txp:permlink></li>
                    </article::custom>
                </ul>

                <if::last_category>
                    <div class="highlight"><a href="#">Get in touch with ...</a></div>
                </if::last_category>

                <txp:hide>** Create an accordion header div and ul block **</txp:hide>
                <txp:variable name="group_header">
                    <div class="accordion-hblock">
                        <h3 class="accordion-header">
                            <txp:category title />-bedroom <span class="p_count">(<txp:variable name="p_count" />)</span>
                        </h3>
                    </div>
                    <txp:hide>** Reset the counter when outputting each new header **</txp:hide>
                    <txp:variable name="p_count" value="0" />
                </txp:variable>
            </txp:evaluate>
            </txp:variable>

            <txp:hide>** Now output the header and block in the correct order **</txp:hide>
            <txp:if_variable name="group_block" not value="">
                <div class="accordion-container">
                   <txp:variable name="group_header" />
                   <txp:variable name="group_block" />
                </div>
            </txp:if_variable>
        </category::list>
    </if::variable>
</category::list>

That bit with the <if::last_category> doesn’t work properly and I can’t fathom a way to do it.

Anyone got any ideas, or perhaps cleaner code so it adds that contact string only after each Location block?

Thank you.


The smd plugin menagerie — for when you need one more gribble of power from Textpattern. Bleeding-edge code available on GitHub.

Txp Builders – finely-crafted code, design and Txp

Offline

#2 2024-08-15 07:46:41

jakob
Admin
From: Germany
Registered: 2005-01-20
Posts: 4,754
Website

Re: Add info to nested category_lists

Tricky without trying it out. In your posted code, I wonder if the closing txp:variable for group_block shouldn’t go higher up? At present, if I’m not mistaken, it also includes group_header within it.

I’m not sure where the contact is stored, but could you not record/output that separately in the outer loop and then output it at the foot of the outer loop. You’re looping through each location, then looping within each location through the bedroom counts, so if I’m not mistaken the location heading and contact could go in the outer loop, but inside the <if::variable name="location_ids" not value=""> and the bedroom loop then only need serve the bedroom accordions. Does that help?


TXP Builders – finely-crafted code, design and txp

Offline

#3 2024-08-15 09:46:01

Bloke
Developer
From: Leeds, UK
Registered: 2006-01-29
Posts: 11,480
Website GitHub

Re: Add info to nested category_lists

Thank you for taking a look. I know you’re insanely busy.

Yeah, the problem I had is that the header is indeed inside the group block variable at the end because then it ‘opens’ the next accordion at the end of the previous one and resets the counter.

I expect if I could separate the generation of the header and ul blocks and build them independently then assemble them in the correct order it would be easier and I could then tack on the contact info as you suggest in the outer loop.

I hit the age old problem of linear parsing something that requires a closing tag and a counter, while only knowing something is different on the next iteration: I need to know I’ve reached the end of the set so I can trigger the closing ul/div, but none of our conditional tags worked. That’s maybe because I’m selecting articles from a set beforehand and then selectively extracting them in the inner category_list, or because they don’t handle nested category_list tags very well. Or I’ve over complicated the logic.

I’m sure there has to be a simpler way of constructing the nested HTML using the power of Txp’s attributes and the spaceship thing, but I can’t figure it out.

Last edited by Bloke (2024-08-15 09:49:46)


The smd plugin menagerie — for when you need one more gribble of power from Textpattern. Bleeding-edge code available on GitHub.

Txp Builders – finely-crafted code, design and Txp

Offline

#4 2024-08-15 10:03:14

Bloke
Developer
From: Leeds, UK
Registered: 2006-01-29
Posts: 11,480
Website GitHub

Re: Add info to nested category_lists

Oh wait, I see what you mean. Yes that works by moving the contact block to the end just outside the inner category_list. Why didn’t I think of that?!

At least, it seems to work. There might be some nuances to that depending on whether the client wants it output on every block but I’ll cross that bridge later.

Thank you. I’d still like to find a neater way to bundle this up though. The logic does my head in every time I come back to this site and have to make changes!

In reality there’s a lot more going on in each accordion <li> e.g. displaying custom fields if such conditions are met, displaying gallery and video links/icons if such content exists or certain custom fields contain flags, and only displaying certain subcats and so forth. The code I posted in the OP is relatively tame compared to the actual thing, hence my quest to find a simpler way overall to output it.

Edit: Bear in mind this same logic is actually used in 4 or 5 sticky articles across various sections to slice and dice the properties, so if I change one I often need to change one or more of the others, which is a pain. If I could offload the building of various chunks to forms and use breakby and break and <+> accordingly, it would save maintenance effort.

Last edited by Bloke (2024-08-15 10:11:14)


The smd plugin menagerie — for when you need one more gribble of power from Textpattern. Bleeding-edge code available on GitHub.

Txp Builders – finely-crafted code, design and Txp

Offline

Board footer

Powered by FluxBB