Theme-ready Shortcodes in WordPress

A few months back, I taught you how to make your widgets theme-ready by extracting the actual markup into an overridable template.  Now, we’re going to do the same for shortcodes.

And for those of you wanting to see this in practice, stay tuned for the release of version 2.5 of WP Publication Archive in a few weeks.  I’m using this technique to make markup easy to change.

Why Use Templates?

The beauty of WordPress is its flexibility.  With a limited amount of technical know-how, you can build a website in minutes that looks completely different from anything else built on the same platform.  It can be as unique as you want and as customized to you as can be.

This flexibility is extended through WordPress’ theme system.  If you have a WordPress site, you’ve used a theme.  What you might now know about the theme, though, is it’s just a template.

WordPress populates a few code objects based on the queried page (whatever link you followed to get to the site), then hands those objects off to the loaded template to render things to the page.  It’s flexible, because WordPress doesn’t know (or need to know) anything about the template.  You can render your content as HTML, XML, JSON, or whatever other capitalized acronym you want.

We’re going to use a template for our shortcode for the very same reason – our plugin will provide a default display for the content, but really that can be replaced by any other template the end user chooses.

Populating the Data

It doesn’t really matter how you build your data so long as you’re precise in the way your data is constructed and used.  The new version of WP Publication Archive, for example, globalizes a variable and populates it with an array.  This array contains, among other things, a collection of publications that need to be displayed whenever the user enters the [wp-publication-archive] shortcode.

The variable has to be global in scope so that the template can access it.  Be very precise with your global object name so no other plugins step all over it.  It might also be a good idea to pass the object through a filter before loading the template as well to provide the highest level of flexibility possible:

1
2
3
4
5
6
7
8
9
10
11
// Get a global container variable and populate it with our data
global $wppa_container;
$wppa_container = array(
    'publications' => $publications,
    'total_pubs'   => $total_pubs,
    'limit'        => $limit,
    'offset'       => $offset,
    'paged'        => $paged,
    'post'         => $post
);
$wppa_container = apply_filters( 'wppa_publication_list_container', $wppa_container );

Loading the Template

Now we actually load of the template.  Be sure to set an easy-to-remember name – I typically prefix my template files with template. so I can remember which files in the plugin might be overridden by a theme.  A descriptive name will also help users know what the file is for:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// Get the publication list template
$template_name = apply_filters( 'wppa_list_template', 'template.wppa_publication_list.php' );
$path = locate_template( $template_name );
if ( empty( $path ) ) {
    $path = WP_PUB_ARCH_DIR . 'includes/' . $template_name;
}

// Start a buffer to capture the HTML output of the shortcode.
ob_start();

include( $path );

$output = ob_get_contents();

ob_end_clean();

// Because globals are evil, clean up afterwards.
unset( $wppa_container );

return $output;

The idea behind this code is to check first if the theme has a similarly-named file.  If so, load that one up.  This file is placed either by the theme developer or the end user specifically to work with your plugin.  Make sure you respect that and defer to their markup.  If the file doesn’t exist, though, load some kind of fallback from within the plugin.

This fallback file should serve as an example to other developers how you think content should appear, but it is not a strict expectation.  Consider it more of an example for how to build a shortcode template.

Output Buffer

Notice in this example I’m using an output buffer.  Normally, I wouldn’t encourage that.  Shortcodes, thought, return data rather than output the data.  But template files should always be geared towards presentation.

It’s easiest to write the template file as if it’s an output file.  Use raw HTML and php’s echo to generate things.  This way it stays in-line with the widget templates we’ve done before and with themes (as templates) as well.

Building Markup

The shortcode template should have no functionality or logic.  Zero. None.

The template file is 100% markup.  You might throw in a loop or two to iterate through collections, but there should be no queries or other data fetching in the file.  Everything you’re looping through should be provided in the global object you passed in earlier.

The end user might elect to add their own additional logic (third party plugins might add meta to your plugin), and they can definitely do that.  But you, as the initial developer, should not.  Keep it simple.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
global $wppa_container;
extract( $wppa_container );
?>
<div class="publication-archive">
<?php foreach( $publications as $publication ) { ?>
    <?php $pub = new WP_Publication_Archive_Item( $publication->ID, $publication->post_title, $publication->post_date ); ?>
    <div class="single-publication">
        <?php $pub->the_thumbnail(); ?>
        <?php $pub->the_title(); ?>
        <?php $pub->the_authors(); ?>
        <?php $pub->the_uri(); ?>
        <?php $pub->the_summary(); ?>
        <?php $pub->the_keywords(); ?>
        <?php $pub->the_categories(); ?>
    </div>
<?php } ?>
</div>

What’s Next?

From here, any developer can drop a template.wppa_publication_list.php file into their theme and replace my default markup with whatever custom markup they choose.  They can use only HTML tags (<aside>, <section>, and the like), they can write out XML, they can reposition elements, etc.  It’s entirely up to them.

But the end user only ever has to enter one shortcode.  Like I said, simple.

Other plugin developers can also hook into the template loading process and inject some custom template as well.  Perhaps another plugin is built to specifically extend WP Publication Archive to add additional meta information and markup – they can hook in and add their own template instead.

About Eric

Eric Mann is a writer, web developer, and outdoorsman living in the Pacific Northwest. If he's not working with new technologies in web development, you can probably find him out running, climbing, or hiking with his dog.

Leave a Reply