Skip to content

Views: Layouts

ookhrimenko edited this page Jan 16, 2018 · 2 revisions

After reading through this guide, you will:

  • Understand the Layouts and recognize how they extend and complement 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 customize the appearance of our sites easily.

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). While it 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 this:

<?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 while we have a base format for our pages, this "knowledge" is written countless times, spread across numerous files, and has no authoritative representation. It 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 Continuously 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 it.

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 $this variable is available inside view files and is pointing to a View class instance.

extends() method simply memorize the layout to inherit from and starts the output buffer.

(for detailed explanation see Theme Framework documentation)

Step 3: The layout files serve the content

After processing the main template View component will get the output buffer as $content variable and include the layout template (actually it will include any template, which was specified inside $this->extend() directive).

Layout file will looks like this:

views/layouts/main.php

<!DOCTYPE html>
<html <?php language_attributes(); ?>>
<head>
	<!-- Your head part -->
</head>
<body <?php body_class(); ?>>
<div id="page">

	<?php $this->include( 'partials/header' ); ?>
    
    	<div id="content" class="site-content">
    
    		<div id="primary" class="content-area">
    			<main id="main" class="site-main" role="main">
    
    				<?php echo $content; ?>
    
    				<?php $this->include( 'partials/sidebar' ); ?>
    
    			</main><!-- #main -->
    		</div><!-- #primary -->
    
    	</div><!-- #content -->
    
    <?php $this->include( 'partials/footer' ); ?>


</div><!-- #page -->

<?php wp_footer(); ?>
</body>
</html>

Furthermore, you're not limited to use only one extends() directive. You can inherit several templates inside each other. This is the main advantage of Layouts over Theme Wrapper.

Step 4: Include partials

Even when we use layouts as base wrapper HTML, it can be pretty big. Or different layouts can share similar parts. To stay DRY we create small templates, which can be included in other templates, and call them partials. Generic partials are placed in views/partials/ folder.

To include another template you can use View component method include():

<?php $this->include( 'folder/template-name', $params ); ?>

This method replaces WordPress core get_template_part() function. It supports child themes as well, so if you created a child theme, you can re-write specific view files inside child theme as usual WordPress templates.

Summary

Effectively we've started and ended with the standard WordPress Template Hierarchy, but grabbed the base format from the appropriate layout file in between. The markup of our content is wrapped between our base markup, the cycle completes, and the layout's job is now done. In fact, because the layout system starts and ends with the output from the standard Template Hierarchy, the vast majority of issues can be resolved just by looking through and understanding the Template Hierarchy Codex Page.

Layouts load process

During daily usage of layouts we found several issues, which can happen with some 3d-party plugins. Some plugins try to use javascript directly inside the_content() and they think the script is already registered by wp_head(). With layouts wp_head() is called AFTER the content part is generated!

Standard request cycle

HTTP Request → WP index.php → wp-config → WP core → Theme functions.php   
    → WP Hooks (setup_theme, init …) → WP Query 
    → {template} → get_header → CONTENT → get_footer

Layout-based request cycle

HTTP Request → WP index.php → wp-config → WP core 
    → Theme functions.php → /app/Theme.php → Theme component objects  
    → WP Hooks (setup_theme, init …) → WP Query 
    → {view} → {Model} → CONTENT
    → {layout file} → get_header / get_footer

Next: Data Models