The powerful Shortcode API allows us to create "snippets" and include special content (such as PHP functions or complex HTML code) in our posts. It's extremely common to build separate functions for each shortcode, but is it really necessary? In this post, we're going to see how we can utilize more than one shortcode by building a single function.
In Defense of Shortcodes
The APIs make WordPress one of the most flexible content management systems of all. They are the reason why there are tens of thousands of plugins and themes out there: The huge community of WordPress is capable of making WordPress do backflips and cartwheels, thanks to the APIs of WordPress.
Among these APIs, my favorite is the Shortcode API. Its logic and validity is being questioned but I strongly believe that using shortcodes in WordPress has an easy learning curve and once they are learned those few easy rules about parameters and opening/closing tags, even novice users can enjoy using shortcodes. Correct, it's not WYSIWYG but a WordPress newbie may have a harder time to clear the mess if they did something wrong in a WYSIWYG editor and ruined the HTML. Easier to see that the shortcode doesn't work, delete it, and type it again.
But of course, it's just an opinion of mine. Let's get back to the subject: We're building multiple shortcodes with a single function!
The Benefit of This Technique
There's really no need to argue the benefit of this method - consider this ridiculously long example:
<?php function one_half_sc( $atts, $content = null, $tag ) { extract( shortcode_atts( array( // extra classes 'class' => '', 'last' => '' ), $atts ) ); if ( $class != '' ) $class = ' ' . $class; if ( $last = 'yes' ) $last_class = ' last'; return '<div class="one_half' . $last_class . $class . '">' . do_shortcode( $content ) . '</div>'; } add_shortcode( 'one_half', 'one_half_sc' ); function one_third_sc( $atts, $content = null, $tag ) { extract( shortcode_atts( array( // extra classes 'class' => '', 'last' => '' ), $atts ) ); if ( $class != '' ) $class = ' ' . $class; if ( $last = 'yes' ) $last_class = ' last'; return '<div class="one_third' . $last_class . $class . '">' . do_shortcode( $content ) . '</div>'; } add_shortcode( 'one_third', 'one_third_sc' ); function one_quarter_sc( $atts, $content = null, $tag ) { extract( shortcode_atts( array( // extra classes 'class' => '', 'last' => '' ), $atts ) ); if ( $class != '' ) $class = ' ' . $class; if ( $last = 'yes' ) $last_class = ' last'; return '<div class="one_quarter' . $last_class . $class . '">' . do_shortcode( $content ) . '</div>'; } add_shortcode( 'one_quarter', 'one_quarter_sc' ); function two_thirds_sc( $atts, $content = null, $tag ) { extract( shortcode_atts( array( // extra classes 'class' => '', 'last' => '' ), $atts ) ); if( $class != '' ) $class = ' ' . $class; if( $last = 'yes' ) $last_class = ' last'; return '<div class="two_thirds' . $last_class . $class . '">' . do_shortcode( $content ) . '</div>'; } add_shortcode( 'two_thirds', 'two_thirds_sc' ); function three_quarters_sc( $atts, $content = null, $tag ) { extract( shortcode_atts( array( // extra classes 'class' => '', 'last' => '' ), $atts ) ); if ( $class != '' ) $class = ' ' . $class; if ( $last = 'yes' ) $last_class = ' last'; return '<div class="three_quarters' . $last_class . $class . '">' . do_shortcode( $content ) . '</div>'; } add_shortcode( 'three_quarters', 'three_quarters_sc' ); ?>
The same code (both PHP and HTML) is being used over and over again in every single function - except the varying CSS classes. Plus, the attributes in each function are the same as in the other functions. It's just hard to read. Come to think of it, if we ever had to change a feature of these column div
s, we would have to change the same parts in all of the functions.
What if there was a variable to fetch the "tag" of the shortcode? Not so surprisingly, the variable is called $tag
and could be used inside our functions. It allows our functions to check the tag of the shortcode and behave according to the tag.
Think about the example above: If there is more than one function that serves almost the same functionality, it would be logical (and pretty cool) to have one function for our shortcodes to use.
I've come up with three good examples on how we can use this technique. If you can think of more while reading the post, you're welcome to share your thoughts with everyone else on the comments section below!
Example 1 Video Embeds
In one of my earliest posts on Wptuts+, I explained how to use a shortcode to embed videos from various video hosts like YouTube, Vimeo and Dailymotion. The code looked like this:
<?php function vid_sc( $atts, $content = null ) { extract( shortcode_atts( array( 'site' => 'youtube', 'id' => '', 'w' => '600', 'h' => '370' ), $atts ) ); if ( $site == "youtube" ) { $src = 'http://www.youtube-nocookie.com/embed/' . $id; } else if ( $site == "vimeo" ) { $src = 'http://player.vimeo.com/video/' . $id; } else if ( $site == "dailymotion" ) { $src = 'http://www.dailymotion.com/embed/video/' . $id; } else if ( $site == "yahoo" ) { $src = 'http://d.yimg.com/nl/vyc/site/player.html#vid=' . $id; } else if ( $site == "bliptv" ) { $src = 'http://a.blip.tv/scripts/shoggplayer.html#file=http://blip.tv/rss/flash/' . $id; } else if ( $site == "veoh" ) { $src = 'http://www.veoh.com/static/swf/veoh/SPL.swf?videoAutoPlay=0&permalinkId=' . $id; } else if ( $site == "viddler" ) { $src = 'http://www.viddler.com/simple/' . $id; } if ( $id != '' ) { return '<iframe width="' . $w . '" height="' . $h . '" src="' . $src . '" class="vid iframe-' . $site . '"></iframe>'; } } add_shortcode( 'vid', 'vid_sc' ); ?>
While it's still a not-so-bad practice (except the 6 else if
statements), we can now use one less attribute ('site
') and create separate shortcodes, like this:
<?php // Video embed shortcodes function wptuts_embed_sc( $atts, $content = null, $tag ) { extract( shortcode_atts( array( 'id' => '', 'w' => '600', 'h' => '370' ), $atts ) ); switch( $tag ) { case "youtube": $src = 'http://www.youtube-nocookie.com/embed/'; break; case "vimeo": $src = 'http://player.vimeo.com/video/'; break; case "viddler": $src = 'http://www.viddler.com/simple/'; break; case "dailymotion": $src = 'http://www.dailymotion.com/embed/video/'; break; } if ( $id != '' ) return '<iframe width="' . $w . '" height="' . $h . '" src="' . $src . $id . '" class="iframevideo"></iframe>'; return; } add_shortcode( 'youtube', 'wptuts_embed_sc' ); add_shortcode( 'vimeo', 'wptuts_embed_sc' ); add_shortcode( 'viddler', 'wptuts_embed_sc' ); add_shortcode( 'dailymotion', 'wptuts_embed_sc' ); ?>
See what we did? We provided a $tag
parameter for our function in the first line (to replace the "site
" attribute) and used a switch
conditional statement (for the sake of better, cleaner code). The result for the two functions will be the same, except you can use [youtube id="..."]
instead of [vid site="youtube" id="..."]
now.
(I didn't use some of the video hosts in the second function. If you need to add more hosts, you can add as many "case
"s as you like.)
Example 2 Column div
s
Ah, columns... they're the most important part of a CSS grid, and they make it super easy to make your website more adaptive, if they support responsive design techniques. You can see them in almost every WordPress theme on ThemeForest and they use shortcodes... with a practice like our "ridiculously long example" at the beginning of this tutorial.
As you remember, the code was almost the same in all 5 functions in that example. Thus, it would be incredibly easy to merge them into one single function and let the $tag
variable work its magic:
<?php // Column shortcodes function wptuts_columns_sc( $atts, $content = null, $tag ) { extract( shortcode_atts( array( // extra classes 'class' => '' ), $atts ) ); if ( $class != '' ) $class = ' ' . $class; $last = ''; // check the shortcode tag to add a "last" class if ( strpos( $tag, '_last' ) !== false ) $tag = str_replace( '_last', ' last', $tag); $output = '<div class="' . $tag . $last . $class . '">' . do_shortcode( $content ) . '</div>'; return $output; } add_shortcode( 'one_half', 'wptuts_columns_sc' ); add_shortcode( 'one_half_last', 'wptuts_columns_sc' ); add_shortcode( 'one_third', 'wptuts_columns_sc' ); add_shortcode( 'one_third_last', 'wptuts_columns_sc' ); add_shortcode( 'one_quarter', 'wptuts_columns_sc' ); add_shortcode( 'one_quarter_last', 'wptuts_columns_sc' ); add_shortcode( 'two_thirds', 'wptuts_columns_sc' ); add_shortcode( 'two_thirds_last', 'wptuts_columns_sc' ); add_shortcode( 'three_quarters', 'wptuts_columns_sc' ); add_shortcode( 'three_quarters_last', 'wptuts_columns_sc' ); ?>
Pretty neat, right? We didn't even need to use a switch
!
We still duplicate lines while adding the shortcodes, though. Dare to go one step further? Remove the add_shortcode()
lines to replace with this:
<?php $tags = array( 'one_half', 'one_half_last', 'one_third', 'one_third_last', 'one_quarter', 'one_quarter_last', 'two_thirds', 'two_thirds_last', 'three_quarters', 'three_quarters_last', ); foreach( $tags as $tag ) { add_shortcode( $tag, 'wptuts_columns_sc' ); } ?>
Now we have even more maintainable code. For example; if we were to change the name of the function, we wouldn't bother to change every line anymore.
Example 3 Post Lists
Ever needed to list some previous (or future) articles in your posts? I certainly do. There are loads of plugins which provide shortcodes but most of them require you to use tons of attributes which can result in some ugly, complex tag like [posts cat="5,6" author="barisunver" status="private" postsperpage="4" and="so on"]
.
How about we use our beloved $tag
instead? Let's give it a shot:
<?php // Post list shortcodes function wptuts_post_lists_sc( $atts, $content = null, $tag ) { global $post; $post_ID = $post->ID; $post_author = $post->post_author; extract( shortcode_atts( array( 'number' => 5, 'exclude_current' => 'yes', 'orderby' => 'date' ), $atts ) ); $args = ''; switch( $tag ) { case "latest_posts": // we don't need any arguments to retrieve latest posts :) break; case "category_posts": $categories = get_the_category( $post_ID ); $first_category = $categories[0]->term_id; $args = 'cat=' . $first_category; break; case "author_posts": $args = 'author=' . $post_author; break; case "future_posts": $args = 'post_status=future'; break; } $not_in = '&post__not_in[]=' . $post_ID; if ( $exclude_current == 'no' ) $not_in = ''; $get_posts = get_posts( $args . $not_in . '&posts_per_page=' . $number . '&orderby=' . $orderby ); $output = '<ul class="' . $tag . '">'; foreach ( $get_posts as $post ) { $output .= '<li><a href="' . get_permalink() . '" title="' . esc_attr( get_the_title() ) . '">' . get_the_title() . '</a></li>'; } $output .= '</ul>'; wp_reset_query(); return $output; } add_shortcode( 'latest_posts', 'wptuts_post_lists_sc' ); add_shortcode( 'category_posts', 'wptuts_post_lists_sc' ); add_shortcode( 'author_posts', 'wptuts_post_lists_sc' ); add_shortcode( 'future_posts', 'wptuts_post_lists_sc' ); ?>
As you can see, we can get rid of 3 possible shortcode attributes: "cat
", "author
" and "post_status
". You can always extend the case
s and add new shortcodes with this single, relatively small function.
Wrapping Up
As a fan of WordPress shortcodes, this discovery of the $tag
variable is kind of exciting for me. It seems like there's a huge potential to utilize this method.
What do you think about this technique? Post your comments below and share your thoughts with us!
Comments