WordPress.org

Make WordPress Core

Opened 5 years ago

Last modified 12 days ago

#14310 reopened enhancement

Make template hierarchy filterable

Reported by: scribu Owned by:
Milestone: Future Release Priority: normal
Severity: normal Version:
Component: Themes Keywords: has-patch dev-feedback
Focuses: template Cc:

Description (last modified by scribu)

Currently, we have filters for each template type: home_template, author_template etc.

The trouble is that these filters are applied on the final template path, after the template hierarchy has been traversed.

It would be useful if there was another filter applied to the actual template hierarchy array, before it was sent to locate_template().

Example

Take the author template hierarchy:

author-{nicename}.php > author-{id}.php > author.php

Say I want to add author-{role}.php before author.php.

Sure, I could use the 'author_template' filter:

function author_role_template( $old_template ) {
  // get current author's role

  $new_template = locate_template( array( "author-$role.php" ) );

  if( $new_template && 'author.php' == $old_template )
    return $new_template;

  return $old_template;
}
add_filter('author_template', 'author_role_template');

With an 'author_template_candidates' hook, I could manipulate the actual hierarchy:

function author_role_template( $templates ) {
  // get current author's role

  $new_template = array( "author-$role.php" );

  $templates = array_merge( 
    array_slice( $templates, 0, -1 ), // before
    $new_template,                    // inserted
    array_slice( $templates, -1 )     // after
  );

  return $templates;
}
add_filter('author_template_hierarchy', 'author_role_template');

This would allow me to remove author-{id}.php if I wanted, etc.

Attachments (7)

template_candidates.diff (4.4 KB) - added by scribu 5 years ago.
template_hierarchy.diff (4.4 KB) - added by scribu 5 years ago.
Name the filter *_template_hierarchy
template_hierarchy.2.diff (4.4 KB) - added by scribu 5 years ago.
refresh
14310.diff (428 bytes) - added by nacin 5 years ago.
template_hierarchy.3.diff (428 bytes) - added by scribu 4 years ago.
just the filter
template_hierarchy.4.diff (455 bytes) - added by scribu 3 years ago.
refresh for WP 3.6-alpha
template_hierarchy.5.diff (792 bytes) - added by stevegrunwell 12 days ago.
Refreshed for WordPress 4.3 + includes inline documentation for the new filter.

Download all attachments as: .zip

Change History (58)

@scribu5 years ago

comment:1 @scribu5 years ago

template_candidates.diff makes all get_*_template() functions call get_query_template(), which consistently applies the appropriate filters.

comment:3 @scribu5 years ago

  • Summary changed from Template hierarchy filter to Make template hierarchy filterable

comment:4 @F J Kaiser5 years ago

  • Cc 24-7@… added

@scribu5 years ago

Name the filter *_template_hierarchy

comment:5 @scribu5 years ago

  • Description modified (diff)

template_hierarchy.diff just renames the filter *_template_hierarchy, which seems more appropriate.

@scribu5 years ago

refresh

comment:6 @scribu5 years ago

  • Resolution set to fixed
  • Status changed from new to closed

(In [15611]) Apply filters consistently in get_*_template() functions. Fixes #14310

comment:7 @azizur5 years ago

  • Cc prodevstudio+wordpress@… added

comment:8 @scribu5 years ago

Marked #15141 as dup.

comment:9 @axwax5 years ago

  • Cc axwax added

comment:10 @markjaquith5 years ago

Nacin and Koopersmith expressed concerns about this. Not necessarily about the idea, but that it didn't get any peer review or iteration. Sort of landed under the radar. get_template_part() went through a lot of revisions, for example. I'd like to give this a chance for more discussion and development before it lands in core.

comment:11 @markjaquith5 years ago

(In [17214]) Revert [15611] for 3.1. Needs more time for peer review and iteration. see #14310

comment:12 @markjaquith5 years ago

  • Milestone changed from 3.1 to Future Release
  • Resolution fixed deleted
  • Status changed from closed to reopened

comment:13 @ryan5 years ago

[17214] broke custom post type templates on wp.com.

comment:14 @coffee2code5 years ago

[15611] has been in trunk for four months, which is quite a while. Pulling it during/after RC 2 stage is rather last minute, particularly since it's being reverted more out of principle than due to bugs or lack of merit.

comment:15 @automattor5 years ago

(In [17220]) I bungled [17214]. Reverting the revert, so it can be reverted properly! see #14310

comment:16 @markjaquith5 years ago

  • Milestone changed from Future Release to 3.1
  • Resolution set to fixed
  • Status changed from reopened to closed

I must have blindly hit "tc" or "mc" in that original revert attempt. It doesn't revert cleanly.

And on second thought, lack of review or not, coffee2code and ryan made a good point: it's been in trunk for a while. Taking it out has already caused problems for people running RC code who wanted to make use of the new filters. Leaving it in. Final answer, Regis.

Sorry for the disturbance on this one. Mea culpa.

comment:17 @nacin5 years ago

  • Resolution fixed deleted
  • Status changed from closed to reopened

@nacin5 years ago

comment:18 @nacin5 years ago

Per discussion with Mark and westi, while a full revert is difficult due to code churn, this filter should never have been added to core. Re-opening for consideration.

comment:19 @westi5 years ago

I'm not convinced about the suitability of the new hook that was added here and I don't think that this has had enough discussion for us to want to keep this and preserve backwards compatibility in the future.

I would rather that we review the need for this new hook in future and discuss it more openly rather than sneak it in as part of a re-organisation of this code.

comment:20 @westi5 years ago

(In [17316]) Remove this new filter as it didn't get enough discussion prior to addition.
Revisit later. See #14310.

comment:21 @westi5 years ago

  • Milestone 3.1 deleted
  • Resolution set to fixed
  • Status changed from reopened to closed

We remembered this again whilest discussing #12877 and if we are going to try and give access to the template hierarchy then we need to have a big discussion as to why we want to change these.

comment:22 @nacin5 years ago

  • Milestone set to 3.1

comment:23 @scribu5 years ago

Fine with me.

comment:24 @scribu4 years ago

  • Milestone changed from 3.1 to Future Release
  • Resolution fixed deleted
  • Status changed from closed to reopened

Duplicate: #16994

comment:25 @scribu4 years ago

Re-opened because the original goal of this ticket wasn't achieved.

comment:26 @johnbillion4 years ago

  • Cc johnbillion@… added

@scribu4 years ago

just the filter

comment:27 follow-up: @scribu4 years ago

  • Keywords dev-feedback added

Could someone explain to me again what's wrong with the "{$type}_template_hierarchy" filter?

Do we plan to drop the hierarchy approach altogether?

Otherwise, I don't see what the big deal is, considering we already have a "{$type}_template" filter on the next line.

comment:28 in reply to: ↑ 27 @johnbillion4 years ago

Replying to scribu:

Could someone explain to me again what's wrong with the "{$type}_template_hierarchy" filter?

Anyone? I'd really love to get a filter added to the template hierarchy. Oh, the things I could do with it!

comment:29 @gruvii4 years ago

  • Cc gruvii added

comment:30 @scribu4 years ago

A similar suggestion: #17788

comment:31 @scribu4 years ago

Related: #13239.

comment:32 @ciobi4 years ago

  • Cc alex.ciobica@… added

comment:33 @navjotjsingh4 years ago

  • Cc navjotjsingh@… added

comment:34 @eddiemoya4 years ago

  • Cc eddie.moya+wptrac@… added

comment:35 @ocean904 years ago

  • Cc ocean90 added

Would be useful to extend the single hierarchy, see #18859.

comment:36 @DrewAPicture4 years ago

  • Cc xoodrew@… added

comment:37 @Bueltge4 years ago

  • Cc frank@… added

comment:38 @divinethemes4 years ago

  • Cc divinethemes added

comment:39 @bainternet3 years ago

  • Cc admin@… added

comment:40 @iandunn3 years ago

  • Cc ian_dunn@… added

comment:41 @raulillana3 years ago

  • Cc raulillana added

@scribu3 years ago

refresh for WP 3.6-alpha

comment:42 @sc0ttkclark3 years ago

  • Cc lol@… added

comment:43 @retlehs3 years ago

  • Cc retlehs added

comment:44 @louisremi2 years ago

It would be interesting to provide a generic filter that would apply to any $type.
I'm trying create WordPress themes using client side frameworks (starting with AngularJS) and I need to recreate the locate_template logic on the client-side. This implies to always send the complete $templates array generated by get_query_template.

Version 0, edited 2 years ago by louisremi (next)

comment:45 @emzo2 years ago

  • Cc wordpress@… added

comment:46 @talbet2 years ago

  • Cc talbet.fulthorpe@… added

comment:47 @markoheijnen2 years ago

Is this something we can add in 3.7?

comment:48 @simonwheatley2 years ago

Here's a scenario we could use this in for Babble (multilingual plugin):

We'd like to allow theme developers to provide language specific versions of templates, e.g. single-product-fr-fr.php. We can use the "{$type}_template" filter for this, but we need to replicate a lot of code when hooking it to properly handle template hierarchy.

What do we need in order to get some discussion going again, and hopefully get the "{$type}_template_hierarchy" filter into 3.7?

comment:49 @christianmagill18 months ago

I too would like to see this implemented. It would allow for implementation of much more modular templating.

comment:50 @Nabha11 months ago

We have a multi-site installation and I'd like to get all the blogs running on a single theme with a modified lookup for theme files. I think this would be needed.

For example, instead of just looking in the root theme folder for author.php, I'd like it to look for /blogs/blog-name/author.php as well. This offers a sort of intuitive way for us to override template parts.

I can kind of sort of get there with the existing filter, but not really, because the full array of searched-for templates is no longer available by the time filters are applied in get_query_template().

Last edited 11 months ago by Nabha (previous) (diff)

comment:51 @stevegrunwell12 days ago

  • Focuses template added

Well shoot, I was just starting on a patch for this concept and found the ticket. Instead, I'll just have to contribute another use-case: a theme wanting to cascade a custom category design through its child sub-categories.

Assume a site has the given category hierarchy:

  • Art
    • Paintings
    • Sculptures
  • Music
    • Blues
    • Jazz
    • Rock
  • Recipes
    • Sweet
    • Savory

Art, Music, and Recipes may be three distinct categories of the same blog and the site owner wants a distinct template for each section, so he/she creates category-art.php in the theme, and the Art category looks great, until the user clicks into Art > Painting and the theme has fallen back to category.php as a template.

Using the this new filter, the site owner could implement something like the following to permit "Paintings" and "Sculptures" to use category-art.php without limiting the ability to use category-paintings.php down the road.

/**
 * Cascade category-specific templates to child categories.
 *
 * @param array $templates A list of template candidates, in ascending order of priority.
 * @return The filtered list of template candidates.
 */
function mytheme_cascade_category_templates( $templates ) {
	$term_id          = false;
	$templates_before = array();

	// Determine the category ID.
	foreach ( $templates as $template ) {
		$templates_before[] = $template;

		if ( preg_match( '/^category-(\d+)\.php$/i', $template, $matches ) ) {
			$term_id = absint( $matches['1'] );
			break;
		}
	}

	// Proceed normally if we can't find a term ID
	if ( false === $term_id ) {
		return $templates;
	}

	// Get the category ancestors
	$parent_terms = get_terms( 'category', array(
		'orderby'    => 'none',
		'include'    => get_ancestors( $term_id, 'category' ),
		'hide_empty' => false,
		'fields'     => 'id=>slug',
	) );

	// Inject the ancestors into the template candidate array
	$templates_after    = array_diff( $templates, $templates_before );
	$ancestor_templates = array();

	foreach ( $parent_terms as $parent_term_id => $parent_term_slug ) {
		$ancestor_templates[] = sprintf( 'category-%s.php', $parent_term_slug );
		$ancestor_templates[] = sprintf( 'category-%d.php', $parent_term_id );
	}

	return array_merge( $templates_before, $ancestor_templates, $templates_after );
}

add_filter( 'category_template_hierarchy', 'mytheme_cascade_category_templates' );

This (example) code effectively leverages this new filter to change the list of template candidates from:

  1. category-sculptures.php
  2. category-2.php
  3. category.php

To:

  1. category-sculptures.php
  2. category-2.php
  3. category-art.php
  4. category-1.php
  5. category.php

I don't think it would make sense from a backwards compatibility perspective to ever enforce this sort of cascade as default behavior, but this filter lets site owners who *do* wish to introduce this functionality to do so without affecting those who prefer the default functionality.

@stevegrunwell12 days ago

Refreshed for WordPress 4.3 + includes inline documentation for the new filter.

Note: See TracTickets for help on using tickets.