Advertisement

Improving Your Work-Flow - Separate Your Mark-Up From Your Logic!

by

In this tutorial I'm going to explain a technique that allows you to utilize a template file for all of your HTML needs! No longer will you need to 'echo' strings from inside your functions, or worry about dropping in and out of PHP just to output some mark-up.

I've spent many years utilising MVC frameworks (such as Zend and nowadays Laravel) where it's a best practice to separate your 'programming logic' (functions or methods) from your 'view' (the resulting HTML mark-up). This always leads to a more maintainable code-base and it's actually a lot easier to write. Having this background prompted me to come up with a similar solution when developing plugins for WordPress! It's nothing too fancy - it's just a little 'helper' that will allow you to remove all the HTML snippets and awkward 'escaping' from your functions and place them safely away in their own 'template' file.

So, I hope this tutorial sounds interesting to you and without further ado, let's begin!


Step 1 Understanding What We're Going to Improve

Let's kick this tutorial off by taking a look at exactly what we're going to improve upon.

It's very common to see something like this within a plugin : (this snippet actually comes from one of my own tutorials on this site :p)

add_shortcode( 'faq', function() {
	$posts = get_posts( array(  // Get the FAQ Custom Post Type
		'numberposts' => 10,
		'orderby' => 'menu_order',
		'order' => 'ASC',
		'post_type' => 'faq',
	));
	$faq = '<div id="wptuts-accordion">'; // Open the container
	foreach ( $posts as $post ) { // Generate the markup for each Question
		$faq .= sprintf( ( '<h3><a href="">%1$s</a></h3><div>%2$s</div>' ),
			$post->post_title,
			wpautop( $post->post_content )
		);
	}
	$faq .= '</div>'; // Close the container
	return $faq; // Return the HTML.
});

What's Wrong With It?

Well nothing, really. But it could be cleaner, easier to scale and more maintainable!

Running from top to bottom, we can see that all within a single function we are :

  1. Querying the database for posts of a certain type
  2. Assigning an HTML string to a variable
  3. Performing a loop and concatenating further markup to the string
  4. Returning the built-up string

Now you may very well be looking at this and thinking "Big deal! It's only a few lines of HTML, what's the problem?" In some respects you're well within your rights to think like that. But remember, it's only 17 lines of code at the moment - what happens when you expand/improve the plugin? What happens when your plugin grows to 50/100/1000 lines of code (or more!). Will you still be happy having HTML strings dotted around your function in various places? What happens when you want to output some HTML that needs some awkward 'escaping' to work properly within your PHP code?

Hopefully you can see that this approach to creating and outputting HTML Markup can get very problematic! Not to mention that it becomes very hard to maintain and improve the HTML when it's just scattered around.

So, with all this in mind, I've taken it upon myself to change the way you think about outputting HTML in WordPress. Forever.


Step 2 Building the View Renderer Plugin

Ok, let's get cracking with this.

Create the Files & Folders

  1. Create a new plugin folder called View
  2. Inside that folder, create the plugin file view_renderer.php
  3. Now create a file called View.php - This will be our class

Include the Class

Our plugin is simple, it just includes the View class so that we can use it in any of our other plugins.

/* view_renderer.php */

include('View.php');

Ok, now that we've included the View class, it's time to actually build it.

The View Class

Here we have a class called View with a single static function called render (this will allow us to use the syntax View::render( $template ) from anywhere within our plugins) and it takes two parameters:

  1. $filePath - The path to the template file. Don't forget we are going to keep our templates within the View folder that we created earlier
  2. $viewData - Any variables that we would like to have access to within the template (much more on this later)

Copy the code below into View.php:

<?php

/** View.php **/

class View {

	/**
	 * -------------------------------------
	 * Render a Template.
	 * -------------------------------------
	 * 
	 * @param $filePath - include path to the template.
	 * @param null $viewData - any data to be used within the template.
	 * @return string - 
	 * 
	 */
	public static function render( $filePath, $viewData = null ) {

		// Was any data sent through?
		( $viewData ) ? extract( $viewData ) : null;

		ob_start();
		include ( $filePath );
		$template = ob_get_contents();
		ob_end_clean();

		return $template;
	}
}
?>

So What Exactly Is Going on Here?

  1. First of all we are checking if the $viewData varible has a value (i.e. did we send anything to be used in the template?). If it does, we extract the contents (more on this later)

  2. Then we are making use of PHP's output buffer. It allows us to parse a PHP file and save the contents to a variable

  3. Finally we return the string

Note: Don't forget to activate the plugin now from within the Admin panel

Seems pretty simple huh? Exactly! But whilst it appears to just be a very simple little function, it actually affords us the luxury of being able to write our plugins in a super-organized, scalable, maintainable fashion. Please, allow me to demonstrate...


Step 3 A Real-World Example

Let's create a simple plugin called Slider

** Note: This is for demonstration purposes only. Feel free to use your own plugin here.

  1. Create a folder called Slider
  2. In that folder, create a file called Slider.php
  3. Copy the code below into Slider.php
<?php
/*
Plugin Name: Slider
Plugin URI: http://wp.tutsplus.com
Description: Generic Slider plugin to demonstrate View Renderer.
Author: Shane Osbourne
Version: 0.1
Author URI: http://wp.tutsplus.com/author/shaneosbourne/
*/
?>

Add a Shortcode

OK, now we're going to add a shortcode that will fetch the latest 5 posts and display them in a list with the title and content. (For the sake of brevity, we'll be adding our plugin class and our action hooks into the same plugin file, but please don't do this in 'real-life' :p )

/**
 * Add the Shortcode (PHP 5.3 and above)
 */
add_shortcode( 'slider', function() {

	return Slider::display();

} );

That will allow us to simply use [slider] in any post/page and it will output the result of Slider::display()

Add the Slider Class & display() Method

class Slider {

	public static function display() {

		// Return HTML HERE.

	}
}

Get the Latest 5 Posts.


/*
 * Get the latest 5 posts
 */
public static function display() {

	$posts = get_posts( array( 'numberposts' => 5 ) );

}

Now we have an array of post objects and we're ready to be building up our HTML by looping through them. But we are not going to simply begin insert HTML strings into our function here! Instead we are going to pass the array of objects to a template file and have all the HTML generated out of harm's way.

Create the Template

  1. Create a folder called templates
  2. Inside that folder, create a file called 01.template.php

This template will hold all of our markup and will allow us to access the data we send to it later.

Sending Data to the Template

Every time we want to use any variables within our templates, we can simply send them by setting a value in the $viewData array. Anyone familiar with using MVC frameworks will feel very at home with this approach.

	$viewData = array( 'posts' => $posts );

The array key here ('posts') is important because that's how we'll refer to the data from within the template. (You can call this whatever you like, but stick to something that makes sense.)

Building the Template

Ok, so we've looked at how to retrieve the latest 5 posts and how to send that array of objects to the template, it's now time to flesh out the template file.

	<div>
		<ul>
			<?php foreach ($posts as $post ) : ?>
				<!-- Loop -->

				<li>
					<h1><?= $post->post_title ?></h1>
					<p><?= $post->post_content ?></p>
				</li>

				<!-- Loop End -->
			<?php endforeach; ?>
		</ul>
	</div>

Ah! How nice does it feel to have all of that markup in its own seperate file, away from our data retrieval and programming logic? Great, I know! The most important part of this approach is that we are only ever 'accessing' data from variables within the template. All the 'logic' should be done within the method that calls the template. This leads to a very nice workflow as you have complete seperation of concerns.

Just imagine how easy it will be now when you're ready to build upon this plugin. No more concatenating strings and escaping characters within functions.

Returning the Rendered Template

Ok, we've seen all the component parts, let's see how it all fits together to allow us to render a template and get a string back (that we can then return to our shortcode):

  1. First we need to store a reference to our template in a static property
  2. Then we need to check that the View class exists
  3. Then we generate the full path to our template file by grabbing a reference to the current plugin directory and concatenating our static property $template
  4. Finally, we call our View::render() method and pass it the two parameters needed

In this case, we return the result of the rendered template because that's how shortcodes work. But if you were needing to echo the results instead (for example, when you create an Admin page, the callback expects your output to be printed out directly), then simply replace return with echo.

The display() Method in Full

	class Slider {

		static $template = '/templates/01.template.php';

		public static function display() {

			if ( class_exists( 'View' ) ) {

				// Get the last 5 posts
				$posts = get_posts( array( 'numberposts' => 5 ) );

				// Set view Data
				$viewData = array(
					'posts' => $posts
				);

				// Get the full path to the template file.
				$templatePath = dirname( __FILE__ ) . static::$template;

				// Return the rendered HTML
				return View::render( $templatePath, $viewData );

			}
			else {
				return "You are trying to render a template, but we can't find the View Class";
			}
		}
	}

I hope you can appreciate the level of organization this approach will afford you! Now your display function is only responsible for collecting the data that it needs and returning the result of the rendered template.


Taking it Further

Our example above is about as basic as it gets. Even so, it's still a vastly improved work flow. Now let's have a look at another example that shows how helpful it can really be.

Say, for example, your plugin makes use of a custom meta box. To do that we would need to:

  1. Add a constructor function to the Slider class
  2. Add a method to add the metabox to every post
  3. Add a callback method to render the HTML for the meta box
  4. Add the appropriate hook in the plugin file to instantiate the class only when adding/editing posts
  5. Finally, we would add a template file as we did earlier, and add it as a property at the start of the class
	class Slider {

		static $metaBox = '/templates/metabox.template.php';

		public function __construct() {
			add_action( 'add_meta_boxes', array( $this, 'add_some_meta_box' ) );
		}

		/**
		 * Adds the meta box container
		 */
		public function add_some_meta_box() {
			add_meta_box(
				'some_meta_box_name',
				'Some Meta Box Headline',
				array( $this, 'render_meta_box_content' ),
				'post',
				'advanced',
				'high',
			);
		}

		/**
		 * Render Meta Box content
		 */
		public function render_meta_box_content() {
			/** From the Codex **/
			echo '<h1>TEST OUTPUT - this gets rendered inside the meta box.</h1>';
		}
	} // class

	// add the action hook
	function call_Slider() {
		return new Slider();
	}

	if ( is_admin() )
		add_action( 'load-post.php', 'call_Slider' );

Take a look at the render_meta_box_content method there. It's the perfect opportunity to use the View Renderer! Imagine a more realistic example like this:

	/**
	 * Render Meta Box content
	 */
	public function render_meta_box_content( $post ) {

		$name = get_post_meta( $post->ID, "name" );
		$fieldName = static::$fieldName;

		echo '<h3>Your name: </h3>';
		echo '<label for="' . $fieldName . '">Name: </label>';
		echo '<input id="' . $fieldName . '" name="' . $fieldName . '" value="' . $name . '" placeholder="Enter your name here" />';
		echo '<button class="button">Update</button>';
	}

Urg! Sure, it gets the job done, but it's so difficult to do it this way! How about we utilize our View Renderer instead.

	/**
	 * Render Meta Box content
	 */
	public function render_meta_box_content( $post ) {

		$viewData = array(
			'name'  => get_post_meta( $post->ID, 'name' ),
			'field' => static::$fieldName
		);

		$templatePath = dirname( __FILE__ ) . static::$metabox;
		echo View::render( $templatePath, $viewData );

	}

And in the template file:

	<h3>Your name: </h3>
	<label for="<?= $field ?>">Name: </label>
	<input id="<?= $field ?>" name="<?= $field ?>" value="<?= $name ?>" placeholder="Enter your name here" />
	<button class="button">Update</button>

It might only seem like a very small benefit in this example. But trust me, if you keep your concerns separate like this, you'll become a far better WordPress developer pretty quickly.


Conclusion

I think by now you probably have a good understanding of what we are trying to achieve here and I urge you to try using this technique when building plugins in the future. Hopefully you'll find 'separation of concerns' to be of benefit to you.

Tutorial Notes:

  • Although we made the View Renderer into a plugin by itself, you could quite easily just add it into existing plugins instead. This will remove the extra step of having to ensure the plugin is activated before you use it anywhere.
  • You are not limited to the use-cases explained in this tutorial, it can be used wherever you would normally output HTML (How about using a template file to output some 'in-line' JavaScript? or how about some specific CSS rules based on options retrieved from the database?)

I'd be interested to know what uses you've found for this technique, so please share in the comments :)

Advertisement