Use generic views with data arrays for a flexible, context-independent UI system
When you start digging into WordPress partials, things really open up on the theme side. Being able to reuse templates is a nice feature and can help keep your code nice and tidy but if your templates are using WordPress’ template tags, you can easily find yourself in situations where you need the markup from a partial but not the data.
To work around this, I use generic views that depend only on an array of data points. This allows me to reuse the same view in multiple places on a site without having to worry if I’m inside a post loop or rendering a specific template, etc. This really opens up flexibility in the UI and also makes it possible to move UI components between projects quite easily.
The basic use example
Once set up, rendering your views is as simple as the following:
<?php
View::render( 'post-single', [
'post_title' => get_the_title(),
'content' => get_the_content(),
'author' => get_the_author(),
] );
Keeping the templates clean and ‘dumb’
The templates themselves all initialise their expected variables at the top of the file and the markup below. You’ll see there are no WordPress template tags in here which removes the context-dependency. ‘Dumb’ templates are nice and versatile.
<?php
$data = isset( $data ) ? $data : new stdClass();
$post_title = isset( $data->post_title ) ? $data->post_title : "";
$author = isset( $data->author ) ? $data->author : "";
$content = isset( $data->content ) ? $data->content : "";
?>
<div class="PostSingle">
<?php if ( $post_title ): ?>
<h1 class="PostSingle__title"><?= $post_title ?></h1>
<?php endif; ?>
<?php if ( $author ): ?>
<div class="PostSingle__author"><?= $author ?></div>
<?php endif; ?>
<?php if ( $content ): ?>
<div class="PostSingle__content"><?= $content ?></div>
<?php endif; ?>
</div>
Setting up the View class so it knows where your templates live
The View class (which I’ve provided further down) is simple to set up and it only needs to know where to look for templates.
<?php
include 'inc/View.php';
View::$view_dir = get_stylesheet_directory() . '/templates';
An example of how to use the views in a WordPress template
Just so you understand the use-case, here is an example of the View class being used within a WordPress template to render a view.
<?php
get_header();
if ( have_posts() ) {
while ( have_posts() ) {
the_post();
View::render( 'post-single', [
'post_title' => get_the_title(),
'content' => get_the_content(),
'author' => get_the_author(),
] );
}
} else {
?>
<p>No post found.</p>
<?php
}
get_footer();
Finally, the View class itself
The View class is a static class the is super simple to configure and just as easy to use as you’ve seen from the previous Gists. You are free to take, use, and modify this class however you need to.
<?php
class View {
public static $view_dir = '';
/**
* Render View Template With Data
*
* Locates a view template and includes it within the same scope as a data object/array. This makes it possible to
* access raw data in the template.
*
* Note: Any data passed into this function will be casted as an array and then as an object. The final data available
* within a template is in the form of an object with the variable name $data.
*
* e.g.
*
* array('name' => 'Bob', 'age' => 42)
*
* Will be converted to an object to be used as;
*
* $data->name
* $data->age
*
* @param string|null $name A named variation for the template. This is in the form {$name}.php. Can include directories, where necessary.
* @param object|array $data An associative array or object to use inside the template.
* @param string $suffix The file suffix.
*
* @return string
*/
public static function prepare( $name, $data = [], $suffix = '.php' ) {
$markup = '';
$path = self::get_full_path( $name . $suffix );
if ( $t = self::view_template_exists( $path ) ) {
$data = self::prepare_data( $data );
ob_start();
include $path;
$markup = ob_get_clean();
}
return $markup;
}
/**
* Use this to echo out templates
*
* @param $name
* @param array $data
* @param string $suffix
*/
public static function render( $name, $data = [], $suffix = '.php' ) {
echo self::prepare( $name, $data, $suffix );
}
/**
* Casts data to an object for use int the template
*
* @param $data
*
* @return object
*/
private static function prepare_data( $data ) {
// if data is not already an object, cast as object
if ( ! is_object( $data ) ) {
$data = (object) (array) $data;
}
return $data;
}
/**
* Making sure the template exists
*
* @param $name
*
* @return bool
*/
private static function view_template_exists( $name ) {
return file_exists( $name );
}
/**
* Pieces together the full path to the file
*
* @param $name
*
* @return string
*/
private static function get_full_path( $name ) {
return trailingslashit( self::$view_dir ) . ltrim( $name, '/' );
}
}