5

I have a website with more or less a thousand news items in a channel and was wondering what was the best way to handle this with stash if I want to separate the logic from the HTML code as much as possible.

1. Using set_list / get_list

Wrapping your channel:entries tags in a set_list then using get_list and stash pagination does not look like it is the right approach since getting 1000 items with a channel entry loop is going to be very time consuming. What's nice is that you can cache the list and not hit the channel entries tags on subsequent visits.

2. Using append_list / get_list

The approach I currently favor is to use channel:entries tags on the outside and using an append_list tag pair on the inside and stashing the pagination works fine. But there, we hit the channel:entries tags on every page load and I don't see how stash caching could be useful in that context.

{!-- Build news list --}
{exp:channel:entries disable="categories|category_fields|member_data|trackbacks" channel="news" status="open" orderby="date" sort="desc" limit="10" paginate="bottom"}

    {exp:stash:append_list name="st_news"}
        {stash:st_title}{title}{/stash:st_title}
        {stash:st_date}{entry_date format="%F %d, %Y"}{/stash:st_date}
        {stash:st_machinedate}{entry_date format="%Y-%m-%d"}{/stash:st_machinedate}
        {stash:st_url}{url_title_path="news_press/news"}{/stash:st_url}
        {stash:st_image}{cf_news_img}{/stash:st_image}
        {stash:st_summary}{exp:eehive_hacksaw words="30" append="..."}{cf_news_summary}{/exp:eehive_hacksaw}{/stash:st_summary}
    {/exp:stash:append_list}

    {paginate}
        {exp:stash:set name="st_pagination" parse_tags="yes"}
            {pagination_links}
                <ul class="pagination group">
                    {first_page}<li><a href="{pagination_url}">First</a></li>{/first_page}
                    {page}<li><a href="{pagination_url}"{if current_page} class="current"{/if}>{pagination_page_number}</a></li>{/page}
                    {last_page}<li><a href="{pagination_url}">Last</a></li>{/last_page}
                </ul>
            {/pagination_links}
        {/exp:stash:set}
    {/paginate}

{/exp:channel:entries}

and then in a snippet or in the same template

{exp:stash:get_list name="st_news" prefix="stashprefix"}

    {if count == "1"}<ul class="horizimglist">{/if}
        <li class="group">
            <p class="imgholder"><a href="{st_url}">{exp:ce_img:pair src="{st_image}" width="680" height="382" crop="yes|center, center" allow_scale_larger="yes"}<img src="{made}" alt="{title}" />{/exp:ce_img:pair}</a></p>
            <div class="postcontent">
                <p class="info"><time datetime="{st_machinedate}" pubdate="pubdate">{st_date}</time></p>
                <h2><a href="{st_url}">{st_title}</a></h2>
                <p>{st_summary}</p>
            </div>
        </li>
    {if count == total_results}</ul>{/if}

    {!-- if no results --}
    {if stashprefix:no_results}<p>No News Found</p>{/if}

    {!-- Pagination --}
    {if count == total_results}{exp:stash:get name="st_pagination"}{/if}

{/exp:stash:get_list}

I was just curious to know:

  • what is the approach stash experts use in such a context
  • are there stash options/parameters (save/refresh/replace) I could use to have better performance
5

You could use the pagination segment as a context for your stashed variables. Each page of results would then be cached independently. The challenge with this approach is clearing the cache once any one of those cached pages expires (since the entire resultset may have changed since last cache).

{exp:stash:context name="{segment_3}"}
{exp:stash:set_list name="st_news" context="@" parse_tags="yes" save="yes" scope="site" replace="no" refresh="60" parse_depth="2"}

{!-- flush ALL site-scoped cached variables. This bit is clumsy, sorry. --}
{exp:stash:unset scope="site"}

{exp:channel:entries disable="categories|category_fields|member_data|trackbacks" channel="news" status="open" orderby="date" sort="desc" limit="10" paginate="bottom"}
        {stash:st_title}{title}{/stash:st_title}
        {stash:st_date}{entry_date format="%F %d, %Y"}{/stash:st_date}
        {stash:st_machinedate}{entry_date format="%Y-%m-%d"}{/stash:st_machinedate}
        {stash:st_url}{url_title_path="news_press/news"}{/stash:st_url}
        {stash:st_image}{cf_news_img}{/stash:st_image}
        {stash:st_summary}{exp:eehive_hacksaw words="30" append="..."}{cf_news_summary}{/exp:eehive_hacksaw}{/stash:st_summary}

    {paginate}
        {exp:stash:set name="st_pagination" context="@" parse_tags="yes" save="yes" scope="site" replace="yes" refresh="61"}
            {pagination_links}
                <ul class="pagination group">
                    {first_page}<li><a href="{pagination_url}">First</a></li>{/first_page}
                    {page}<li><a href="{pagination_url}"{if current_page} class="current"{/if}>{pagination_page_number}</a></li>{/page}
                    {last_page}<li><a href="{pagination_url}">Last</a></li>{/last_page}
                </ul>
            {/pagination_links}
        {/exp:stash:set}
    {/paginate}

{/exp:channel:entries}

{/exp:stash:set_list}

And retrieve like this:

{exp:stash:get_list name="st_news" context="@" scope="site"}
...
{/exp:stash:get_list}
{exp:stash:get name="st_pagination" context="@" scope="site" process="end"}

Note that we're caching the pagination block for slightly longer than the list, and using replace="yes". This is because the list should always expire first, and once it does it will run the enclosed tags and will always regenerate the associated pagination block (since replace="yes").

The site-scoped cache flushing I consider somewhat clumsy since it should really target the meta-grouping of cached variables that constitutes all the pages of results only, rather than all site-scoped variables. There is in fact a way to group variables that has been in Stash since the very beginning ("bundles") but which I am only now fleshing out more fully. In future you will be able to use the bundle="" parameter to group or flush an arbitrary set of related variables (from either your template code or using a new CP interface for cache management).

| improve this answer | |
  • Hey Mark, thanks for stopping by. Getting an answer from "the" Croxton himself! That's a clever and interesting approach, indeed. That being said, I am not sure the benefits outweigh the "costs" in this case. Definitely interested in those cache management functions, though. – Jérôme Coupé Jan 22 '13 at 14:23
  • Also, I noticed you use set_list around the entries tags in your reply. Is this always a better approach (performance-wise) than using append list, even if you vace to use parse_depth=2? – Jérôme Coupé Jan 22 '13 at 14:27
  • {exp:stash:set_list} is usually better because it's called once rather than multiple times as {exp:stash:append_list} would be (being inside the channel entries loop). The parse_depth doesn't make much difference to performance because those tags have to be parsed anyway (if not by Stash then by EE later on) and Stash intelligently stops parsing when the requested depth exceeds the actual number of tag layers. – Mark Croxton Jan 22 '13 at 19:53
  • I'd suggest giving the above a go by the way, you can always remove save="yes" and replace="no" and leave it dynamic if it doesn't behave the way you want. You still get the benefit of markup separation. – Mark Croxton Jan 22 '13 at 19:56
  • Thanks for the explanations, Mark. I'll keep it dynamic for now. I'll add caching if needed (maybe I'll be able to use the bundle parameter by then). By the way, cannot wait to see what you have in store for stash. Looking great! – Jérôme Coupé Jan 23 '13 at 11:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.