Setting Category-Based Post Templates Using Yoast SEO’s Primary Category Feature

When developing WordPress themes, we often need to set individual templates for posts. For example, if a post is in the video category, we need a video player template; if it’s in the image category, we need a gallery template. In WordPress, there are many ways to set individual templates for posts.

The first method is using custom post templates, which became available in WordPress 4.7. The example code below shows how to add post to the Template Post Type, allowing you to select a separate template when publishing a post, just like with pages.

<?php 
/*
Template Name: Full-width layout
Template Post Type: post, page, event
*/
// Below is the page template code

This method is easy to implement, but users have to select the corresponding template when publishing, which adds an extra step.

Another method is using post formats. This requires theme support for post formats. When publishing, the user selects a post format, and in the template, we call the corresponding template based on the format. The key code is as follows:

<?php get_template_part( 'content', get_post_format() ); ?>

This method also requires a selection in the backend, and the post formats supported by WordPress are limited. While you can add custom post formats, it increases complexity.

Setting Category-Based Post Templates

Neither of the above methods is ideal. Can we, like category templates, directly add a template file like category-single.php in the theme so that WordPress automatically selects the corresponding post template after a user chooses a category?

After some searching, we found that the implementation is very simple. Add the following code to your theme, and then add a single-category-slug.php template file. If no template is set for a subcategory, it will automatically use the parent category’s template; if that doesn’t exist either, it defaults to the standard template.

add_filter( 'single_template', function ( $template )
{
   foreach ( (array) get_the_category() as $cat ) {
      if ( file_exists( TEMPLATEPATH . "/single-category-{$cat->slug}.php" ) ) return TEMPLATEPATH . "/single-category-{$cat->slug}.php";
      if ( $cat->parent ) {
         $cat = get_the_category_by_ID( $cat->parent );
         if ( file_exists( TEMPLATEPATH . "/single-category-{$cat->slug}.php" ) ) return TEMPLATEPATH . "/single-category-{$cat->slug}.php";
      }
   }

   return $template;
} );

Using Yoast SEO’s Primary Category Feature

After using the above code for a while, we found a small issue. If a post is assigned to two categories simultaneously, WordPress picks the template for the first category. Sometimes this isn’t what we want; we need the post to follow a specific category’s template. Since the Yoast SEO plugin has a “Primary Category” feature, we adjusted the code as follows:

add_filter( 'single_template', function ( $template )
{
   // Get primary category ID
   $primary_cat_id = get_post_meta( get_the_ID(), '_yoast_wpseo_primary_category', true );

   foreach ( (array) get_the_category() as $cat ) {

      // Try using the primary category template
      if ( $primary_cat_id ) {
         $primary_cat = get_term( $primary_cat_id );

         if ( file_exists( TEMPLATEPATH . "/single-category-{$primary_cat->slug}.php" ) ) {
            return TEMPLATEPATH . "/single-category-{$primary_cat->slug}.php";
         }
      }

      // If primary category template doesn't exist, use general category template
      if ( file_exists( TEMPLATEPATH . "/single-category-{$cat->slug}.php" ) ) {
         return TEMPLATEPATH . "/single-category-{$cat->slug}.php";
      }

      if ( $cat->parent ) {

         // Try the parent of the primary category
         if ( $primary_cat_id ) {
            $primary_cat = get_term( $primary_cat_id );

            if ( $primary_cat->parent ) {
               $primary_cat_parent = get_term( $primary_cat->parent );
               if ( file_exists( TEMPLATEPATH . "/single-category-{$primary_cat_parent->slug}.php" ) ) {
                  return TEMPLATEPATH . "/single-category-{$primary_cat_parent->slug}.php";
               }
            }
         }

         // Fallback to parent category template
         $cat = get_term( $cat->parent );
         if ( file_exists( TEMPLATEPATH . "/single-category-{$cat->slug}.php" ) ) {
            return TEMPLATEPATH . "/single-category-{$cat->slug}.php";
         }
      }
   }

   return $template;
} );

Yoast SEO automatically sets the first category as the primary one, and in most cases, clients don’t set two different categories for one post, so adjusting the primary category is rare. This code perfectly meets our needs, allowing custom themes to automatically select templates based on categories without adding extra backend steps for the client.

There’s still room for improvement, such as how to handle priority when combined with post formats or custom page templates. if you have a better implementation, feel free to share it in the comments!

Related Posts

Leave a Reply

Your email address will not be published. Required fields are marked *