single-{$post_type}-{slug}.php for custom post types

My favorite part of the WordPress template hierarchy is the ability to quickly create template files for pages by slug, without having to edit the page in WordPress to select a template.

We can currently do this:

page-{slug}.php

But I would like to be able to do this:

single-{post_type}-{slug}.php

So that, for example, in a post type called review, I could make a template for a post called “My Great Review” at single-review-my-great-review.php

Has anybody set this up before? single-{post_type}-{slug}.php

6

A) The Base in Core

As you can see in the Codex Template Hierarchy explanation, single-{$post_type}.php is already supported.


B) Extending the core Hierarchy

Now there’re gladly some filters and hooks inside /wp-includes/template-loader.php.

  • do_action('template_redirect');
  • apply_filters( 'template_include', $template )
  • AND: a specific filter inside get_query_template( $type, ... ) named: "$type}_template"

B.1) How it works

  1. Inside the template loader file, the template gets loaded by a query var/wp_query conditional: is_*().
  2. The conditional then triggers (in case of a “single” template): is_single() && $template = get_single_template()
  3. This triggers then get_query_template( $type, $templates ), where $type is single
  4. Then we have the "{$type}_template" filter

C) The solution

As we only want to extend the hierarchy with one template that gets loaded before the actual "single-{$object->post_type}.php" template, we’ll intercept the hierarchy and add a new template to the beginning of the templates array.

// Extend the hierarchy
function add_posttype_slug_template( $templates )
{

    $object = get_queried_object();

    // New 
    $templates[] = "single-{$object->post_type}-{$object->post_name}.php";
    // Like in core
    $templates[] = "single-{$object->post_type}.php";
    $templates[] = "single.php";

    return locate_template( $templates );    
}
// Now we add the filter to the appropriate hook
function intercept_template_hierarchy()
{
    add_filter( 'single_template', 'add_posttype_slug_template', 10, 1 );
}
add_action( 'template_redirect', 'intercept_template_hierarchy', 20 );

NOTE: (If you want to use something other than the default objects slug) You’ll have to adjust $slug according to your permalink-structure. Just use whatever you need from the global (object) $post.

Trac Tickets

As the above approach is currently not supported (you can only filter the absolute located path this way), here’s a list of trac tickets:

  • Extending the hierarchy with post_type->slug
  • Introduce a filter for get_query_template()
  • Filter the complete hierarchy – most promising ticket ⤎ follow this ticket by @scribu as cc

Leave a Comment