John D Wells

I build websites with HTML, CSS, jQuery, PHP, ExpressionEngine & CodeIgniter.

Proud co-founder of the kick-ass, London-based creative agency One Darnley Road.

© johndwells

Homegrown plugin to create template “partials” for ExpressionEngine

Template “partials” functionality for ExpressionEngine: Embrace the DRY principle, reduce embed parsing and simplify your template ecosystem with this roll-your-own plugin.

Published:
Jan 09, 2011
Filed under:
EECMS
Comments:
15 - add yours

SIDE NOTE: If you'd rather not hack together your own plugin, there are a number of 3rd party add-ons that can help you achieve the same approach outlined in this article; I've provided links to these alternatives at the bottom of the page.

Generally speaking, a “partial” is a template that can be passed, as a variable, to another template for output. Their purpose is to provide a convenient way to include common content across multiple templates.  Most frameworks (RoR, CodeIgniter, FuelPHP etc) all have their own flavour of template partials, and ExpressionEngine’s embeds & snippets more or less the job - but not without a few downsides.

I'm going to discuss a technique that attempts to remedy these downsides. It will involve getting your hands dirty rolling our own EE plugin.  It will be simple, limited, but hopefully demonstrate the potential at our fingertips.

What We Have Now

I’m willing to bet that if you were to open up any one of your templates in EE, it might look something like this:

{embed="embeds/_html_head"}
<body class="blog">
	<div id="wrapper">
		{embed="embeds/_main_navigation" nav_active="blog"}
		<div id="content">
			{exp:channel:entries ...}
				...
			{/exp:channel:entries}
		</div>
		{snippet_sidebar}
		{embed="embeds/_footer"}
	</div>
	{embed="embeds/_html_foot"}
</body>
</html>

It’s a fairly standard setup, especially if you’ve crossed over from the world of Wordpress and are accustomed to functions like get_header() & get_footer() sprinkled throughout your theme files.  And at a glance, it’s a super convenience, allowing you to include common content across your site.  EE’s embeds and snippets are great.

But the downsides? I see three:

  1. Passing content into your embeds is cumbersome at best
  2. Each EE embed adds significant overhead to the parsing engine
  3. You still have to repeat that general markup structure for every template file that’s directly responsible for outputting content to the browser.

Now, downside #1 can be remedied somewhat by simply wrapping more of your template with your {exp:channel:entries} tag, but it does little if you want to pass more than simple strings into an embed.  Even so, downsides #2 and #3 remain, and these two are of greater concern. So let’s see if we can fix them all.

What We Need

We need a solution that solves our 3 problems above, plus meets an additional requirement I'd like to add:

  1. Reduced quantity of embeds
  2. DRY template pattern with a single "wrapper" template
  3. Chunks of content can easily be passed to that wrapper template
  4. Lightweight and bespoke plugin that meets our needs, our flavour of development, and frees us from relying on a 3rd party developer

Let me pause on the last point: I'm a huge proponent of self-reliance; of becoming familiar and comfortable with the core code base that you work on; and of tools that do one job and do that job well.  EE plugins are surprisingly easy to build yourself, open up a whoop-ass can of power, and can even speed up your templates.  You would do yourself a great service to learn how to roll your own EE plugins.

What It Will Look Like

So, we are going to build a "Partials" plugin that allows us to assign chunks of content to variables that are saved and then rendered later on by a single template.  This "wrapper" template will contain our entire page structure, as well as markup that had previously been distributed across our earlier embeds (in our examples, those were _html_head, _main_navigation, _footer and _html_foot).

That's right, we are going to go from 4 template embeds down to 1.

Here's what our calling template will look like:

{embed="embeds/_wrapper" body_class="blog" nav_active="blog"}
{exp:channel:entries ...}
	{exp:partials:set name="page_title"}{title}{/exp:partials:set}
	{exp:partials:set name="content"}
		{channel_body}
	{/exp:partials:set}
{/exp:channel:entries}

You may notice we call our wrapper template first, but then "set" content later. Don't worry though, as the wrapper template will be embeded last, because of EE's parse order. However if you prefer, you're welcome to place the {embed} call at the end of your template. It will work all the same.

Lines 3-6 are where the magic happens: we set two new partials and fill them with content. Our first partial is a simple string; our second contains our entire body content from our channel.

Let's look at our wrapper template now:

<!DOCTYPE html> 
<html>
<head> 
	<title>{exp:partials:get name="page_title"} | {site_name}</title>
</head>
<body class="{embed:body_class}">
	<div id="wrapper">
		<ul id="main_navigation" class="{embed:nav_active}">
			...
		</ul>
		<div id="content">
			{exp:partials:get name="content"}
		</div>
		{snippet_sidebar}
		<div id="footer">
			...
		</div>
		{snippet_analytics}
	</div>
</body>
</html>

Look, no more embeds! What's more, this reads as a complete template; everything is in one place, and any changes to the foundation of your markup structure only needs to be made here. At line 4 we can see where we get one of our template partials, 'page_title'; then at line 12 we get our second, 'content'.

The Plugin, Finally

If you're wholly unfamiliar with EE plugins, head over to the User Guide and have a quick scan to get the basic idea.

And now, to the main event. Copy-and-paste this code into /system/expressionengine/third_party/partials/pi.partials.php, and modify the first few lines to suit; that's right, put your name in there! After all this is going to be your plugin, not mine.

<?php
if ( ! defined('BASEPATH')) exit('No direct script access allowed');
$plugin_info = array(
  'pi_name' => 'Homegrown Partials',
  'pi_version' =>'1.0.0',
  'pi_author' =>'Your Name Here',
  'pi_author_url' => 'http://yoursite.com',
  'pi_description' => 'Homegrown Partials - Template partials plugin for EE'
  );

class Partials {

	public $EE;

	/*
	 * PHP5 constructor
	 * get instance of EE, and set up our session cache
	 */
	public function __construct()
	{
		$this->EE = get_instance();
		if ( ! array_key_exists('partials', $this->EE->session->cache))
		{
			$this->EE->session->cache['partials'] = array();
		}
	}
	// END

	/*
	 * Set content in session
	 */
	public function set()
	{
		$name = strtolower($this->EE->TMPL->fetch_param('name'));
		$this->EE->session->cache['partials'][$name] = $this->EE->TMPL->tagdata;
	}
	// END
	
	/*
	 * Get content from session
	 */
	public function get()
	{
		$name = strtolower($this->EE->TMPL->fetch_param('name'));
		if (array_key_exists($name, $this->EE->session->cache['partials'])
		{
			return $this->EE->session->cache['partials'][$name];
		}
	}
	// END
}
/* End of file pi.partials.php */
/* Location: ./system/expressionengine/third_party/partials/pi.partials.php */

So the first half of our plugin is standard boilerplate stuff; set up our info array (EE needs this), and get an instance of the $EE object. Things get interesting around line 22, when we add an array to EE's session cache. All cache data is only available for this page request, and is wiped as soon as the request is over.

After that, we create two basic methods; one for {exp:partials:set} and one for {exp:partials:get}. Partials::set() retrieves our name parameter and passes all tagdata (everything between {exp:partials:set} ... {/exp:partials:set} into our cache array, where $name is the key.  Then {exp:partials:get} simply retrieves that cached tagdata based on the same $name value.

And since this is ours, and not something we're releasing to the community, we don't need to be super safe - forgo the tedious checks if $name is a valid value, or if you might be overwriting the array value with something previously saved. So simple.

And if you are so inclined, improve upon it; build in a second parameter that flags whether you should overwrite or append your saved data.  Add another parameter that will allow you to append or prepend strings to what is being printed. You could even build multiple variations of the Partials::get() method to output variables differently, depending on your needs.

The possibilities are endless, and again since this is For Your Site Only, there's nothing to stop you!

3rd Party Solutions

This approach is in fact not new, but definitely not well-promoted; a few community developers have built fantastic and feature-rich add-ons that will help you achieve this technique if you'd rather not build your own plugin.  A few of these are:

Published:
Jan 09, 2011
Filed under:
EECMS
Comments:
15 - add yours
 

Have your say...

  • #1
  • On 02:44 PM, 09/01/11
  • From USA
  • Ian Pitts said:

Awesome write-up. I’m definitely using this on my next EE2 site. I’ve been using REEposition on my EE1 sites for a similar purpose but the ability to fully customize the plugin is compelling.

Beautiful blog, BTW.

  • #2
  • On 07:29 PM, 09/01/11
  • From London, UK
  • John D Wells said:

Thanks Ian! I’m glad you’ve reminded me about REEposition - I’ve added it to the list of 3rd party add-ons.

Cheers

  • #3
  • On 10:08 PM, 09/01/11
  • From Brisbane, Australia
  • John Faulds said:

Yeah, keen to give this a try myself next time. Cheers John.

  • #4
  • On 06:26 AM, 10/01/11
  • From Newcastle, Australia
  • Leevi Graham said:

Excellent approach achieved in such a simple plugin.

I’m super keen to give this a try in my next project as it looks like it could cut down duplication.

Nice work John.

  • #5
  • On 07:29 AM, 10/01/11
  • From Melbourne, Australia
  • Cem Meric said:

John, Brilliant plugin and a game changer approach for EE development process.

Thanks for sharing it.

  • #6
  • On 08:20 AM, 10/01/11
  • From Belgium
  • Moonbeetle said:

Interesting approach. I like the fetching data - data presentation separation. Since certain types of pages usually require a certain layout I can imagine though that this might result in a set of wrappers to choose from. Shifting the complexity from individual templates to the wrappers. But still this looks interesting.

  • #7
  • On 11:41 AM, 10/01/11
  • From London, UK
  • John D Wells said:

Moonbeetle, I think you’ve nailed it on the head.  On a simple site layout (like this one) it’s been a great solution, but on a more elaborate site, the complexity could begin to outweigh its benefit.

Cheers

  • #8
  • On 04:22 PM, 10/01/11
  • Nevsie said:

Surely you can do exactly the same as the above with snippets wrapped entirely in a exp:channel:entries tag? i.e.
{exp:channel:entries}
{snip_head}
{snip_top}
  {snip_title}
  {snip_channel_content}
{snip_foot}
{/exp:channel:entries}

The chances are you are going to have to use an embed for displaying other channel data anyway?

But I do see your logic about seeing the template as “one complete file” - that is very nice.

Also - relative to the parse order how does your plugin work? For example does it process pre/post globals, snippets, etc? For example could you:
{exp:partials:set name=“page_title”}{snip_title}{/exp:partials:set}
And therefore use snippets before passing it as a partial to _wrapper?

Intriguing and good article, this will be an ongoing discussion point as i feel performance in EE is causing a bit of hesitancy for many.

Hi John, thanks for this. Very insightful.

Fyi - line 45 is missing a single quote and closing parenthesis.

...->cache[‘partials’]))

  • #10
  • On 06:33 PM, 10/01/11
  • From London, UK
  • John D Wells said:

Thanks for the catch Nuno!

Nevsie, the parse order works the same as any other plugin - and can be effected with parse inward/outward if necessary.

In your particular example, if you wrap {exp:partials:set name=’...’} around a snippet tag, the snippet will be rendered first before being saved in the partials cache.

You’ve raised a good reminder for myself, which is to not neglect snippets - personally I’m just frustrated that I cannot save them to the filesystem (without paying for a 3rd party addon) and use them within my preferred editing environment (Coda).

Cheers!

  • #11
  • On 05:05 AM, 11/02/11
  • From Chicago
  • Bryant said:

Great article and tutorial!  One question though… in the template where you call partials:get - that assumes your channel:entries tag with content is located inside your partial -  is it possible to keep the channel:entries tag in the template that calls partials:get, so that the actual markup for the entry is abstracted from the channel:entries tag?

  • #12
  • On 03:35 PM, 11/02/11
  • From London, UK
  • John D Wells said:

@Bryant: I think I see what you’re getting at, but no.  And we’re sort of already accomplishing abstraction, just in the other direction of what you’re describing…

 

  • #14
  • On 02:37 PM, 16/03/11
  • Russ Back said:

Great idea and seems to be working well in my tests. However I’ve just come across a problem when using the paginate tag of the channel entries module. I have my channe; entries being passed to the plugin and appearing the page as per your demo. But when adding the {paginate} tag it gets rendered after my wrapper template, presumably because EE is parsing it after everything has finished. Any thoughts/ideas?

  • #15
  • On 10:53 PM, 17/03/11
  • From NZ
  • Mark Stewart said:

Very handy plugin, exactly what I was looking for… thanks!

Although the plugin has one typo that took me a while to track down. The if conditional in the get function

if (array_key_exists($name, $this->EE->session->cache[‘partials’])

needs a closing “)” at the end.

Thanks again.