PixelfearWeb Development by Jason Varga

Stash is awesome. My new ExpressionEngine workflow.

Tips, Workflow, ExpressionEngine, Stash

I’ve come to realize that Stash is an incredible tool for improving your ExpressionEngine workflow.

I’ve adapted my workflow through advice from other people, and thought I’d document it in case anyone would find it useful.

Directory Structure

My template folder looks something like this:
(Pages are linked so you can jump down to them if you’d like.)

For the sake of keeping everything together, I store my snippets (through Low Variables) and Stash templates alongside my EE templates.

I use Low Variables on pretty much every EE install, so I’d use it to save my snippets as files. The _low_vars/ folder holds those. If you don’t use LV, there are other add-ons that allow you to save snippets as files.

The general approach

Templates are split up into 3 types of files.

  • Routes
  • Models
  • Views

This particular example will show how a simple product listing would work.


Routing templates allow me to forgo the standard ExpressionEngine URL structure (template_group/template).
So, I can use http://mysite.com/products/product-name instead of http://mysite.com/products/product/produt-name.

In this case, my routing template would be products.group/index.html.
It would look something like this:

{exp:ifelse parse="inward"}
    {if segment_2}

If there’s a second segment, it’s a product, otherwise its the index.
404’s would be checked from within the detail template.

Edit: Mark Croxton pointed out that nothing is stopping you from using Stash embeds here. You could then do full page caching.

Edit 2: Alternatively, you could use Switchee to catch pagination, too:

{exp:switchee variable="{segment_2}" parse="inward"}
    {!-- Catches either /Px or blank in segment_2 --}
    {case value="#^P(\d+)$#|''"}
    {!-- Everything else --}
    {case default="yes"}


Models get and set the values.

You ‘get’ data from the DB using EE tags such as {exp:channel:entries} and you ‘set’ or ‘stash’ variables (and pairs/loops) using {exp:stash:set} tags.





    {exp:stash:set_list name="images"}


At the top, I’m embedding my layout/view. That’s the template that’ll contain all my markup and any front-end stuff. It’ll take the data I set here, and display it.

I set any single values using the {exp:stash:set} tag pair and sub tag pairs. These are retrievable later using {exp:stash:get} tags.

I would then set any sets of loopable data using {exp:stash:set_list} tag pairs.
In this example, {cf_product_images} is a P&T Assets field, which is a tag pair that loops through all the assets/images selected. Using this stashed list, I can loop through these variables on the front-end.


Once you set all the data in the model, you retrieve and display them in the view. This allows you to keep presentation separate from content.




    <div class="product-details">
        <p>Price: {exp:stash:price}</p>

    <div class="product-images">

        <img src="{exp:stash:image}" alt="{exp:stash:product_name}" class="large-image" />

        <ul class="thumbnails">
        {exp:stash:get_list name="images"}
            <li><a href="{url}">{exp:ce_img:single src="{url}" width="50" height="50" crop="yes"}</a></li>



Now the listing pages

The model, products.group/_listing.html again will do the setting.


{exp:stash:set_list name="products" parse_tags="yes"}
  {exp:channel:entries channel="products" dynamic="no"}

The view, _stash/layouts/listing will do the getting.

  {exp:stash:get_list name="products"}
      <h2><a href="{link}">{product_name}</a></h2>
      <a href="{link}">View</a>


The snippets aren’t anything too special. They contain code that would be reused throughout your site. The point is to put your code into reusable chunks.

{sn_page_start} _low_vars/default_site/sn_page_start.html

<!doctype html>
    <meta charset="utf-8" />
    <div class="page-wrap">
            <a href="/" class="logo"><img src="logo.png" /></a>
                <a href=""></a>

{sn_page_end} _low_vars/default_site/sn_page_end.html

    </div><!-- / page-wrap -->
    <script src="jquery.js"></script>
    <script src="global.js"></script>

There are two stash tags in here that I like to make use of.

{exp:stash:meta} allows me to add in any meta tags (or anything that should go in the <head>, really) that are specific to a template. eg. <title> tags or Facebook Opengraph meta tags.

{exp:stash:page_js} allows me to add any template specific JavaScript to the bottom of the page.

What’s the point?

This might seem like a big change for no real benefit. Why do it then?

Maintenance sucks, so make it easier.

The main advantage—to me, at least—is that you can separate your markup from the data. It allows you to keep all your code much more maintainable.

Keeping it DRY

The other advantage which may not be evident in this example is that you can reuse the same layout/view in multiple places.

In this example, I’m referring to a products channel. But lets say your main product is fruit but you also sell merchandise.

  • You’d have two product channels, one called fruit and one called merchandise.
  • Instead of a products template group, you’d have a fruit template group and a merchandise template group.
  • Your model in each respective template group would set the same stash variables, but with different custom field names. Then your layout/view would be able to leverage the same variable names.

What about if you have a news section that looks almost identical to a blog section? Use the same approach as above.
Blog posts and news articles could use an article layout.
Blog listings and news listings… use a listing layout.

Performance & Caching

This is an area that I haven’t touched on at all. But Stash brings many options for caching chunks of your templates.

..and more

Some people choose to use one layout for their entire site. You can do that. There are many different ways to use Stash. I’d love to hear how you use it.

Let me know if my workflow is a good or bad idea and why. I’m sure it can still be improved.


blog comments powered by Disqus