1

I have used to following code to change the location of 'single' Wordpress templates.

define(SINGLE_PATH, TEMPLATEPATH.'/single/');
add_filter('single_template', 'lsmwp_custom_single');

function lsmwp_custom_single($single) {

  global $wp_query, $post;

  if ($post->post_type == "cpt_operator"){
      if(file_exists(SINGLE_PATH . '/single-' . $post->post_type . '.php'))
          return SINGLE_PATH . '/single-' . $post->post_type . '.php';
  }

  foreach((array)get_the_category() as $cat) :
      if(file_exists(SINGLE_PATH . '/single-cat-' . $cat->slug . '.php'))
        return SINGLE_PATH . '/single-cat-' . $cat->slug . '.php';
      elseif(file_exists(SINGLE_PATH . '/single-cat-' . $cat->term_id . '.php'))
        return SINGLE_PATH . '/single-cat-' . $cat->term_id . '.php';
  endforeach;

  if(file_exists(SINGLE_PATH . '/single.php'))
    return SINGLE_PATH . '/single.php';
  elseif(file_exists(SINGLE_PATH . '/default.php'))
    return SINGLE_PATH . '/default.php';
  return $single;
}

The function works fine and Worpdress is looking for single templates in the new folder. However if I have WP_DEBUG set to TRUE I receive the following error:

Use of undefined constant SINGLE_PATH - assumed 'SINGLE_PATH'

Have I gone wrong with my function somewhere?

  • You do have a huge amount of issues here, will post an answer. ;-) – Pieter Goosen Feb 8 '16 at 10:08
  • Great, looking forward to seeing where Iv gone wrong :) – James Feb 8 '16 at 10:15
6

You have a lot of issues here.

  • Stay away from defining globals and constants if you can. The global scope is an evil place to be. WordPress has made a huge mess of the global scope already, don't make it a bigger mess than it already is. You can make use a static variable inside your function/filter

  • Avoid using TEMPLATEPATH. You should use get_template_directory_uri(). IIRC, there was a notice somewhere of future depreciation of TEMPLATEPATH, but citation is needed on this as locate_template() for one still uses TEMPLATEPATH. But anyway, use get_template_path_directory_uri() for parent themes, that is the correct way

  • You would want to use locate_template() to search for a specific template, and not file_exists. Let WordPress handle WordPress stuff. Only use PHP (or for that matter custom SQL) if WordPress does not provide a native function/means of doing something. The native functions are there to take care of other stuff that you might be forgetting about when using custom PHP

    In the new context, if we use locate_template(), we do not need to use get_template_directory_uir(). locate_template() by default handles that section for us. That is why it is important to use native WordPress functions to handle a specific job

    You have a double slash in your generated filenames. Your constant is /single/ and your file name is /single.php, which renders /single//single.php. You need to remove a slash in iether of the two to avoid the duplicate slash

  • single-{$post_type}.php is by default used for custom post types

  • If you are working with the post object of a single post page outside the loop, rather use get_queried_object() which is much more reliable than the $post global.

  • Although your syntax is valid, it is better to use curly brackets ({}) instead of the : and endforeach syntax. This also goes for the syntax of your conditionl statements. I prefer curlies as they are easy to debug should my code fail

  • If you create a custom hierarchy like you have, always set index.php as your ultimate fallback template as index.php is required by all themes

Lets properly rewrite your code (NOTE: I'm using closures which have the downside that it cannot be removed by remove_filter(), so change it to normal spaghetti if you wish.). We will be using the logic inside get_single_template() to help us here

(The code requires PHP 5.4+ and is untested)

add_filter( 'single_template', function ( $template )
{
    // Get the current single post object
    $post = get_queried_object();
    // Set our 'constant' folder path
    $path = 'single/';

    // Set our variable to hold our templates
    $templates = [];

    // Lets handle the custom post type section
    if ( 'post' !== $post->post_type ) {
        $templates[] = $path . 'single-' . $post->post_type . '-' . $post->post_name . '.php';
        $templates[] = $path . 'single-' . $post->post_type . '.php';
    }

    // Lets handle the post post type stuff
    if ( 'post' === $post->post_type ) {
        // Get the post categories
        $categories = get_the_category( $post->ID );
        // Just for incase, check if we have categories
        if ( $categories ) {
            foreach ( $categories as $category ) {
                // Create possible template names
                $templates[] = $path . 'single-cat-' . $category->slug . '.php';
                $templates[] = $path . 'single-cat-' . $category->term_id . '.php';
            } //endforeach
        } //endif $categories
    } // endif  

    // Set our fallback templates
    $templates[] = $path . 'single.php';
    $templates[] = $path . 'default.php';
    $templates[] = 'index.php';

    /**
     * Now we can search for our templates and load the first one we find
     * We will use the array ability of locate_template here
     */
    $template = locate_template( $templates );

    // Return the template rteurned by locate_template
    return $template;
});

EDIT

The code above is tested and working as expected.

EDIT 2 - Pre PHP 5.4 (T_Rex still roam the darkness)

add_filter( 'single_template', function ( $template )
{
    // Get the current single post object
    $post = get_queried_object();
    // Set our 'constant' folder path
    $path = 'single/';

    // Set our variable to hold our templates
    $templates = array();

    // Lets handle the custom post type section
    if ( 'post' !== $post->post_type ) {
        $templates[] = $path . 'single-' . $post->post_type . '-' . $post->post_name . '.php';
        $templates[] = $path . 'single-' . $post->post_type . '.php';
    }

    // Lets handle the post post type stuff
    if ( 'post' === $post->post_type ) {
        // Get the post categories
        $categories = get_the_category( $post->ID );
        // Just for incase, check if we have categories
        if ( $categories ) {
            foreach ( $categories as $category ) {
                // Create possible template names
                $templates[] = $path . 'single-cat-' . $category->slug . '.php';
                $templates[] = $path . 'single-cat-' . $category->term_id . '.php';
            } //endforeach
        } //endif $categories
    } // endif  

    // Set our fallback templates
    $templates[] = $path . 'single.php';
    $templates[] = $path . 'default.php';
    $templates[] = 'index.php';

    /**
     * Now we can search for our templates and load the first one we find
     * We will use the array ability of locate_template here
     */
    $template = locate_template( $templates );

    // Return the template rteurned by locate_template
    return $template;
});
  • When you said I had gone wrong I thought my syntax was off, was not expecting a whole new function! Another great answer, I did not know about the danger of the settings variables in global scope, will be wary in future. Many thanks – James Feb 8 '16 at 18:34
  • I think somethings gone wrong somewhere, I just received 500 error – James Feb 8 '16 at 18:38
  • That is strange, you have something which is breaking something else in your theme or in a plugin – Pieter Goosen Feb 8 '16 at 18:40
  • I am running php 5.3.3 – James Feb 8 '16 at 18:41
  • 1
    Perfect! Thats the ticket.. note: syntax error at '$templates = array' missed the (). Oh and i'm more of a V_Lociraptor caught in the red tape of management :) Thanks for your help bud – James Feb 8 '16 at 18:54
2

A note about the actual message you're getting from PHP. When you define a constant, you should do it with a string.

define(SINGLE_PATH, TEMPLATEPATH.'/single/');

The first parameter is the constant name - when you call define the constant doesn't exist yet.

This would take care of the message from PHP:

define('SINGLE_PATH', TEMPLATEPATH.'/single/');

A full example of using a constant:

<?php
define( 'SOME_CONSTANT', true );

if ( SOME_CONSTANT ) {
    echo 'Constant was true';
}

Also

if I have 'WP_DEBUG' set to TRUE

Notice how WP_DEBUG is defined:

define( 'WP_DEBUG', true );

But in your code later you would call it without quotes:

var_dump( WP_DEBUG ); // bool(true)
0

If you are using "template-parts" folder instead this will work. -function.php

add_filter( 'single_template', function ( $template )
{
    // Get the current single post object
    $post = get_queried_object();
    // Set our 'constant' folder path
    $path = 'template-parts/';

// Set our variable to hold our templates
$templates = array();

// Lets handle the custom post type section
if ( 'post' !== $post->post_type ) {
    $templates[] = $path . 'content-' . $post->post_type . '-' . $post->post_name . '.php';
    $templates[] = $path . 'content-' . $post->post_type . '.php';
}

// Lets handle the post post type stuff
if ( 'post' === $post->post_type ) {
    // Get the post categories
    $categories = get_the_category( $post->ID );
    // Just for incase, check if we have categories
    if ( $categories ) {
        foreach ( $categories as $category ) {
            // Create possible template names
            $templates[] = $path . 'content-' . $category->slug . '.php';
            $templates[] = $path . 'content-' . $category->term_id . '.php';
        } //endforeach
    } //endif $categories
} // endif  

// Set our fallback templates
$templates[] = $path . 'single.php';
$templates[] = 'index.php';

/**
 * Now we can search for our templates and load the first one we find
 * We will use the array ability of locate_template here
 */
$template = locate_template( $templates );

// Return the template rteurned by locate_template
return $template;
});

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.