Skip to content

Component Template Hierarchy

alex_prokopenko edited this page Jan 12, 2018 · 2 revisions

Template Hierarchy component is defined with Web/Template_Hierarchy.php class.

Explaining new views system

Standard template system is updated into views. This means, that all templates are moved under views folder. Furthermore, all templates were grouped in one more directory level by section.

So in general all template files are located like this:

/views/{section}/ .. files ..

{section} is a post type ID or logical group (like search)

By default we have several pre-defined view section names:

├── views/                  # → Templates
│   ├── layouts/            # → Template wrapper templates
│   ├── partials/           # → Small template parts same for several sections. (Header, footer, etc.)
│   │
│   ├── page/               # → Page templates (like front-page, page-*, etc.)
│   ├── post/               # → Post templates (like index, archive, single, etc.)
│   └── search/             # → Search templates

All template names are following standard WordPress Template Hierarchy namings.

Let's check how standard Template Hierarchy works with views system:

Post templates

You can use such template name patterns to create some unique template:

├── views/                  # → Templates
│   ├── post/               # → Post templates
│   │   ├── index.php           # → Default for blog listing or if other templates missing
│   │   ├── archive.php         # → Default for blog archives if category/tag/author/date templates missing
│   │   │
│   │   ├── category-{slug}.php
│   │   ├── category-{id}.php
│   │   ├── category.php
│   │   │
│   │   ├── tag-{slug}.php
│   │   ├── tag-{id}.php
│   │   ├── tag.php
│   │   │
│   │   ├── date.php
│   │   │
│   │   ├── author-{nicename}.php
│   │   ├── author-{id}.php
│   │   ├── author.php
│   │   │
│   │   ├── single-{slug}.php   
│   │   └── single.php          # → Default for post single  

Page templates

You can use such template name patterns to create some unique template:

├── views/                  # → Templates
│   ├── page/               # → Page templates
│   │   ├── front-page.php     # → Default for front page if Static homepage is enabled
│   │   ├── 404.php             # → 404 error template
│   │   │
│   │   ├── {custom template file}.php
│   │   ├── page-{slug}.php
│   │   ├── page-{id}.php
│   │   └── page.php            # → Default for any page   

Search templates

├── views/                  # → Templates
│   ├── search/               # → Search templates
│   │   └── search.php            # → Search results/No results template   

Custom post types

All custom post type templates will be placed in separate folder. Standard WordPress template names will be still available:

├── views/              # → Templates
│   ├── {cpt ID}/           # → Custom post type templates
│   │   ├── archive.php     # → Custom post type archive
│   │   ├── index.php       # → Custom post type archive - alternative
│   │   │
│   │   ├── single-{slug}.php   
│   │   └── single.php      # → Custom post type single template   

In our practice we found that standard archive pages are not very useful. Because you can't control page title, description, featured image, etc. All is hard-coded inside the template or theme options and it's not trivial for the final administrator to edit this content.

We recommend to create custom template files with custom query/custom loop to display custom post type archives. In our development we usually place custom template file for CPT archive inside CPT templates folder.

Custom taxonomies

For custom taxonomies default template will be:

├── views/   
│   ├── {cpt ID}/archive-{taxonomy ID}.php

Example: If you created taxonomy "department" and attached it to CPT "employee", than correct template is views/employee/taxonomy-department.

How it works

The WordPress template system lets you filter the hierarchy. This means that you can insert and change things at specific points of the hierarchy. The filter (located in the get_query_template() function) uses this filter name: "{$type}_template" where $type is the template type.

Here is a list of all available filters in the template hierarchy:

  • embed_template
  • 404_template
  • search_template
  • frontpage_template
  • home_template
  • taxonomy_template
  • attachment_template
  • single_template
  • page_template
  • singular_template
  • category_template
  • tag_template
  • author_template
  • date_template
  • archive_template
  • index_template

Template Hierarchy registers filters for all these templates plus custom post type templates. After that is just adds views/$type/ folder to all standard templates.

<?php
class Template_Hierarchy {
	use Singleton;

	// ...
	
	/**
	 * Declaration of sections for standard template types.
	 *
	 * @var array
	 */
	protected $template_types_folders = array(
		'views/page'   => array( '404', 'frontpage', 'page', 'paged' ),
		'views/post'   => array( 'home', 'index', 'archive', 'author', 'category', 'tag', 'date', 'single' ),
		'views/search' => array( 'search' ),
		'views'        => array( 'embed', 'attachment', 'singular', 'taxonomy' ),
	);
	
	/**
	 * Template_Hierarchy constructor.
	 * set WordPress template system hooks
	 */
	protected function __construct() {
		// patch page/custom post type templates.
		add_action( 'init', array( $this, 'init_theme_page_template_hooks' ), 1000 );

		// set filter for all query template types.
		foreach ( $this->template_types as $type ) {
			add_filter( "{$type}_template_hierarchy", array( $this, "{$type}_template_hierarchy" ) );
		}

		// add woocommerce support.
		add_filter( 'woocommerce_template_path', array( $this, 'woocommerce_template_path' ) );

		// Support of ACF, to display page templates from subfolders.
		add_filter( 'acf/location/rule_values/page_template', array( $this, 'acf_page_templates_scan' ), 20, 1 );
	}
	
	// ...
	
	/**
	 * General entry point for template rewrites system
	 *
	 * @param string $type page query type.
	 * @param array  $templates standard WP templates.
	 *
	 * @return array
	 */
	protected function extend_template_hierarchy( $type, $templates ) {
		// generate template for post types.
		if ( 'single' === $type && $templates = $this->get_single_templates() ) :
		elseif ( 'page' === $type && $templates = $this->get_page_templates( $templates ) ) :
		elseif ( 'archive' === $type && $templates = $this->get_archive_templates() ) :
			// defaults - wrap with views folders based on type.
		elseif ( $templates = $this->wrap_template_in_folders( $type, $templates ) ) :
		endif;

		return $templates;
	}
	
	// ...

	/**
	 * Add "views/{post_type}/" folder for all templates, set by WP.
	 * This allows us to follow almost same WP template hierarchy rules
	 *
	 * @param string $type page query type.
	 * @param array  $templates standard WP templates.
	 *
	 * @return array
	 */
	protected function wrap_template_in_folders( $type, $templates ) {
		$foldered_templates = array();

		foreach ( $this->template_types_folders as $folder => $folder_types ) {
			if ( ! in_array( $type, $folder_types, true ) ) {
				continue;
			}

			foreach ( $templates as $template ) {
				if ( strpos( $template, 'views' ) !== 0 ) {
					$foldered_templates[] = "{$folder}/{$template}";
				}
			}
			break;
		}

		return $foldered_templates;
	}
	
	// ...

Extend Template Hierarchy

If you want add new rules or patch some existing rules to load another views structure you need

Step 1: Create your Template Hierarchy class inside a theme

{theme}/app/Template_Hierarchy.php

<?php
namespace BarCompany\Theme;

use JustCoded\WP\Framework\Web\View;

class Template_Hierarchy extends \JustCoded\WP\Framework\Web\Template_Hierarchy {
	protected function __contruct() {
		parent::__construct();
		
		// add your hooks here
	}
	
	// or rewrite methods you want to update
}

Step 2: Set your own Template Hierarchy to be loaded

Template Hierarchy is loaded by base Theme class. If you want to replace Template Hierarchy you need to rewrite specific method inside your Theme class:

{theme}/app/Theme.php

<?php
namespace BarCompany\Theme;


class Theme extends \JustCoded\WP\Framework\Theme {
	// ...
	
	/**
	 * Init new Template Hierarchy based on "views" folder and load Views engine.
	 */
	public function init_views_templates() {
		// set our own template hierarchy to load
		\BarCompany\Theme\Template_Hierarchy::instance();
		// still keep view system loader
		\JustCoded\WP\Framework\Web\View::instance();
	}
}

Turn off custom Template Hierarchy and Views system

To turn off template hierarchy and view system you just need to replace Theme class method like this:

{theme}/app/Theme.php

<?php
namespace BarCompany\Theme;


class Theme extends \JustCoded\WP\Framework\Theme {
	// ...
	
	/**
	 * Init new Template Hierarchy based on "views" folder and load Views engine.
	 */
	public function init_views_templates() {
		// keep method blank!!!
	}
}

After that you will be able to use standard templates system.