Low Variables 2.3 totally killed the embed tag

The {embed} tag in ExpressionEngine has had a rough couple of years. What used to be the only reasonably useful way to break your design up into smaller, reusable chunks has come under siege. For simply breaking up your templates or reusing bits of code, {embed} has been partially replaced by snippets; meanwhile, the tag has been rightfully hammered for the performance hit it can cause (from Solspace’s performance guide):

For every single embedded template that is found, ExpressionEngine performs a query to retrieve that template and its settings. It then processes that template as if it was its own page and when finished it replaces the {embed=""} tag with the final, parsed output. Effectively, an embedded template is a sub-page within a page, and it requires all of the processing that building a page from a template entails. It is not a lightweight operation.

Basically, these days you shouldn’t use an {embed} if you don’t have to.

But they’re still useful in one situation that comes up entirely more often than you might like: you have to nest a tag pair inside another tag pair (especially if it’s the same module or there are otherwise conflicts between the variable names). In that case, you pass some variables through the embed, probably like so:

{exp:channel:entries}      

  {embed='embeds/some_troublesome_tag'
    entry_id="{entry_id}"
    {!-- It's, like, always entry_id --}
  }

{/exp:channel:entries}

Then in the embed template, you access that passed variable with {embed:entry_id} and – voilà – no more trouble.

Enter Low Variables 2.3+

However, a fairly recent update to the excellent Low Variables add-on has pretty much eliminated that reason to use {embed}, too.

In the 2.3 update, there was this change:

Added the preparse:my_var="" parameter to the Textarea variable type

With these instructions (emphasis mine):

If the variable content contains variables that need to be parsed before it is put in the template, you can use this parameter, similar to embed variables.

For real?! I’m in love.

Marshall going awwww

Basically, you create a TextArea Low Variable, access it with the {exp:low_variables:single} tag (so it’s parsed along with other {exp:tag}s, and you can pass on anything you want:

{exp:low_variables:single 
  var='used_to_be_an_embed'
  preparse:segment_2='{segment_2}'
}

A recent example

I’ll give an example I ran into recently on the site that I maintain:

I had an overly-complicated scheme with a Playa relationship tag nested inside a module that checked some stored information about the logged-in member (that needed a url_title) nested inside another Playa variable nested inside a {exp:channel:entries} loop.

{exp:channel:entries}

  {!-- this is a Playa variable.
  basically like using exp:playa:children --}
  {relationship}

    {exp:member_categories:check category="{url_title}"}
      {exp:playa:children entry_id='{entry_id}'}
        [some code about the grandchild entry]
      {/exp:playa:children}

      {if no_results}
        [some more code…]
      {/if}

    {/exp:member_categories:check}

  {/relationship}

{/exp:channel:entries}

Yikes. I had potential conflicts in entry_id, url_title, and {if no_results}, and I was nesting Playa in Playa on top of that. Before Low Variables, I definitely needed an embed to untangle it.

{exp:channel:entries}

  {relationship}

    {embed='spaghetti/category_check_and_playa'
        child_entry_id='{entry_id}'
        child_url_title='{url_title}'
    }

  {/relationship}

{/exp:channel:entries}

That would pass the variables as you’d expect into the embed template:

{exp:member_categories:check category="{embed:child_url_title}"}
  {exp:playa:children entry_id='{embed:child_entry_id}'}
    [some code about the grandchild entry]
  {/exp:playa:children}

  {if no_results}
    [some more code…]
  {/if}

{/exp:member_categories:check}

That worked! All the tags got the parameters they needed when they needed them, and there were no surprises with variable conflicts.

But you can do the same thing with a Low Variable in place of the embed:

{exp:channel:entries}

  {relationship}

    {exp:low_variables:single
        var='my_spaghetti_code'
        preparse:child_entry_id='{entry_id}'
        preparse:child_url_title='{url_title}'
    }

  {/relationship}

{/exp:channel:entries}

using a textarea Low Variable named my_spaghetti_code:

{exp:member_categories:check category="{child_url_title}"}
  {exp:playa:children entry_id='{child_entry_id}'}
    [some code about the grandchild entry]
  {/exp:playa:children}

  {if no_results}
    [some more code…]
  {/if}

{/exp:member_categories:check}

(exactly the same as the embed, but the early-parsed variables aren’t prepended with embed:)

And it works too!

The nerdy bits

This works because of how the parser goes through a template in sequence and parses module/extension tags in the order they're encountered. Let's go step-by-step.

At the {exp:tag} phase, the parses sees this:

{exp:channel:entries}

  {relationship}

    {exp:low_variables:single
        var='my_spaghetti_code'
        preparse:child_entry_id='{entry_id}'
        preparse:child_url_title='{url_title}'
    }

  {/relationship}

{/exp:channel:entries}

It processes the channel:entries tag first (including the {relationship} Playa pair’s {entry_id} and {url_title} variables), leaving this (say for 2 entries):

{exp:low_variables:single
    var='my_spaghetti_code'
    preparse:child_entry_id='1'
    preparse:child_url_title='first-related-url-title'
}

{exp:low_variables:single
    var='my_spaghetti_code'
    preparse:child_entry_id='2'
    preparse:child_url_title='second-related-url-title'
}

Now the parser tackles the first low_variables:single. The result replaces the tag with the contents of the variable (and the preparse:variable_name variables with the parameter values):

{exp:member_categories:check category="first-related-url-title"}
  {exp:playa:children entry_id='1'}
    [some code about the grandchild entry]
  {/exp:playa:children}

  {if no_results}
    [some more code…]
  {/if}

{/exp:member_categories:check}

{exp:low_variables:single
    var='my_spaghetti_code'
    preparse:child_entry_id='2'
    preparse:child_url_title='second-related-url-title'
}

And now, continuing down the template in order, the parser sees the member_categories:check and playa:children tags as if they were there all along! First the outer member_categories:check (we’ll say it had a result):

  {exp:playa:children entry_id='1'}
    [some code about the grandchild entry]
  {/exp:playa:children}







{exp:low_variables:single
    var='my_spaghetti_code'
    preparse:child_entry_id='2'
    preparse:child_url_title='second-related-url-title'
}

And then the playa:children tag:

    [now totally processed code about the grandchild entry]








{exp:low_variables:single
    var='my_spaghetti_code'
    preparse:child_entry_id='2'
    preparse:child_url_title='second-related-url-title'
}

Repeat for the next low_variables:single tag. Clever!

Performance gains

Although it ends up working almost just like the embed, we’ve lost the whole “run through the template parser again” overhead. How much of a difference does that make, you ask?

I ran a test with real data from the site, using a template with only the above code and with the outermost channel:entries tag going through every applicable entry. That produced about 200 relationships, which in turn left about 200 embeds (or low_variables:single tags) to chug through.

I ran that only partially unrealistic mess with the Output Profiler, tallied the processing time, and averaged it over 50 refreshes:

Tag Average processing time
embed 1.87s
low_variables:single .67s

Barney going YEAH

There you have it: a small {embed} is almost 3 times slower than an identical {exp:low_variables:single} tag.

For just a few tags you might not notice, but it definitely adds up.

Some gotchas

If you’re going to replace embeds this way, some things to note:

  • As with all template debugging, don’t even try something like this unless you thoroughly understand parse order. Lodewijk Schutte (who else) has you covered with a presentation and a cheat sheet (.pdf) in addition to occasional blog posts. All the following bits come from understanding the parse order.
  • If your Low Variable uses any early-parsed variables ({segment_1}, {embed:name}, early globals), those have missed being parsed so you’ll have to pass those in with preparse:name (like preparse:segment_2='{segment_2}')
  • Likewise, you won’t see performance gains from “simple conditionals” parsing early (because you’ve missed that boat too). They’ll still work themselves out, but at the later “Advanced conditionals” parsing phase.
  • If you need some PHP parsed on input in your variable, that’s not going to happen. And also you’re nuts. PHP on output will be OK. (I try to avoid PHP in templates like the plague, but Low presents some compelling uses here if you’re interested.)

Nice knowing you

This is just one of the many ways that Low Variables lets you tinker with tags to your benefit. It’s a well-loved add-on with a lot of potential uses and this is icing on the cake.

Just don’t step on {embed}’s corpse on the way out.

Am I doing it wrong?

Comments? I don’t do open comments. Life is too short.

If you have something to say, get in touch via .(JavaScript must be enabled to view this email address) or on Twitter.