Skip to content

Component View

alex_prokopenko edited this page Jan 11, 2018 · 2 revisions

After reading through this guide, you will:

  • Understand the Layouts and recognize how it extends and complements the WordPress template hierarchy.
  • Know what is meant by the DRY Principle, why being DRY beats being WET, and see how most WordPress themes are WET.

Template Hierarchy

WordPress is pretty smart. Every time you load up a request it will search for the most relevant template available in your theme and load it. This is the Template Hierarchy in action and it enables us to easily customize the appearance of our sites.

Want to customize a specific page named "About"? Just copy page.php, to page-about.php and edit away to your heart's content. You only need to look at the success of WordPress to realize that this system works because of its simplicity and accessibility. But it doesn't work perfectly.

To prevent each new template from having a duplicate header, footer and sidebar, WordPress encourages you to separate that code into other templates and include them with the get_header(), get_footer() and get_sidebar() functions (all of which are based on get_template_part). Whilst this makes your code more manageable, with only one edit needed to implement a change across all templates, it still duplicates code that simply doesn't need to be duplicated; the code which calls each of those templates.

Layoutless templates

In your typical layoutless theme, every page template will look something like the following:

<?php get_header(); ?>
  <div class="main">
    <div class="content">
      <?php // Our page specific markup and loop goes here ?>
    </div>
    <?php get_sidebar(); ?>
  </div>
<?php get_footer(); ?>

Even though we know that every template will take this base format and render the header, footer, sidebar calls each time, we still need to continuously repeat the code to keep WordPress happy; it's laborious and unnecessary.

Enter DRY

DRY simply means Don't Repeat Yourself and conforming to the DRY Principle means:

Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.

So whilst we have a base format for our pages, this "knowledge" is written countless times, spread across numerous files and has no authoritative representation. This is the opposite of DRY code and it's usually described as being WET, meaning that you Write Everything Twice.

As you can see from the standard template, WordPress goes beyond Continously Repeating Yourself. Whatever you want to call it, it wastes your time when creating the code, when reading the code and when editing the code; it's a lose-lose-lose situation (plus repetition is only fun in rhetoric) but it's easy enough to avoid.

Create layout and stay DRY

The goal of a layout is to remove any repeated markup from individual templates and put it into a single file. This file, views/layouts/main.php becomes the single, unambiguous, authoritative representation of knowledge (i.e. the main format code). By doing this we can put the focus entirely on the page specific markup and loop, simplifying our templates to look like this:

<?php 
    $this->extends('layouts/main');
    
    // Our page specific markup and loop goes here 
?>

It's neat. It's tidy. You never need to make calls to get_header(), get_footer() or get_sidebar() again. You can also refactor the main format of your site by editing layouts/main.php. But best of all, it takes less than 50 lines of code to do so.

This pattern is used in all modern PHP Frameworks: Laravel, Yii 2 and Symfony (Symfony uses Twig, and Twig also has layouts).

First realization of similar feature for WordPress was described in 2011 and was called a Theme Wrapper (read article).

Let's take a closer look into layouts.

Step 1: WordPress figures out which template to use

This is done using the standard WordPress Template Hierarchy, which, as mentioned before, selects the most relevant template as our starting point. Once this template has been chosen, but before it's loaded, WordPress runs the template_include($template) filter.

We use this filter to run our wrap function that saves the real $template path and always return root index.php file as a template. This file will be an entry point of our templates system.

<?php
namespace JustCoded\WP\Framework\Web;

use JustCoded\WP\Framework\Objects\Singleton;

/**
 * Views base class.
 * Used for layouts and render partials
 */
class View {
	use Singleton;

	/**
	 * Layouts call chain.
	 *
	 * @var array
	 */
	private $extends = array();

	/**
	 * Theme template path to be loaded.
	 *
	 * @var string
	 */
	public $template;

	/**
	 * View constructor.
	 *
	 * Executed immediately before WordPress includes the predetermined template file
	 * Override WordPress's default template behavior.
	 */
	protected function __construct() {
		add_filter( 'template_include', array( $this, 'init_template' ), 999999 );
	}

	/**
	 * Implement template wrappers. For this we need to remember real template to be loaded.
	 * Then we return the object itself to be able to manipulate the loading process.
	 *
	 * @param string $template Template to be included inside theme.
	 *
	 * @return $this
	 */
	public function init_template( $template ) {
		$this->template = $template;

		return $this;
	}

	/**
	 * Convert object to string magic method.
	 * We replaced string with object inside `template_include` hook, so to support next include statement we need
	 * to add magic method, which make object to string conversion.
	 *
	 * Here we will just return theme index.php file, which will be the entry point of our views engine.
	 *
	 * @return string
	 */
	public function __toString() {
		return locate_template( array( 'index.php' ) );
	}
	
	// ...
}

* View component is a part of Theme Framework, which is required by Boilerplate theme

This code makes WordPress template loader to load our theme root index.php file. Furthermore, after including index.php we have a global variable $template inside, which is actually our View instance. So we can find out the real template and run a method to include templates and process template inheritance.

index.php

<?php
/**
 * Main template in WordPress theme.
 * Used to load views engine
 *
 * @see /views/ folder instead
 * @var $template \JustCoded\WP\Framework\Web\View
 */

$template->run();

Step 2: Specify layout template to use

Inside each specific template you should specify the layout to extend at the beginning of the file:

views/page/page.php

<?php
/* @var \JustCoded\WP\Framework\Web\View $this */

$this->extends( 'layouts/main' );

// Our page specific markup and loop goes here 

We include all templates inside a View class instance, that's why we have $this variable, which is pointing to a View class instance.

Extends method simply memorize the template to inherit and starts the output buffer.

After template is generated - output buffer is cleaned and passed to extended layout as $content variable.
(for detailed explanation see Theme Framework documentation)

View component

<?php
class View {

    // ...
    
	/**
	 * Registers parent template.
	 * Parent template will be rendered just after current template execution.
	 *
	 * To use current template generated html use `$content` variable inside the parent view
	 *
	 * @param string $layout  View name to register.
	 *
	 * @return bool
	 * @throws \Exception If no parent view template found.
	 */
	public function extends( $layout = 'layouts/main' ) {
		if ( false === $layout ) {
			return false;
		}

		// WordPress compatibility to still process usual headers.
		if ( empty( $this->extends ) ) {
			do_action( 'get_header', null );
		}

		// check that we have required template.
		$template = $this->locate( $layout, true );

		// memorize the template.
		array_push( $this->extends, $template );

		// start buffer.
		ob_start();
		return true;
	}
	
	// ...

The main advantage of layouts instead of Theme Wrapper - you can inherit as many templates as you like.

Clone this wiki locally