Define custom Page Template without its own .php file

Is there any way of defining a Page Template from some function/filter/hook, without having any .php file containing the comment at the top of the page to declare the Template Name?


For example, instead of doing this:

some-custom-page-template.php

/**
* Template Name: Some Custom Page Template
*/

another-custom-page-template.php

/**
* Template Name: Another Custom Page Template
*/

I wonder if I can do something like:

functions.php

wp_some_function_to_define_templates( 'Some Custom Page Template' );
wp_some_function_to_define_templates( 'Another Custom Page Template' );

EDIT: I’m writing a complex and kind of experimental WP theme – It is as Object-Oriented as WP lets it to be, I have Controller classes to decide what to display in each page, DAO classes to retrieve data from DB, Twig templates to generate the views and so on…

I’ve spreaded the code so much, that my Page Templates have become just a line of code where I call some Controller and let it do the job, to finally render a Twig template. So I decided to move that call to the Controller, to inside the template_include filter, so that I don’t even need to put any code in the Page Templates…

I might get rid of Page Templates, but I still need them to group pages somehow, to set some Advanced Custom Fields groups for them, etc…

So I ended up with about 15 PHP files that contain only a comment at the top with some Template Name, which is exactly what I’d like to avoid by defining those templates from some function…

Am I trying to reinvent the wheel? Maybe in some sense, but I think it’s becoming a really cool theme, very maintenable and modifiable…

3 s
3

WordPress read the templates from file headers, and then set them in the cache. using wp_cache_set() nad a key that is derived from the md5 has of stylesheet folder.

So, just overriding that cache we can show the templates we want. To override that cache we need to call wp_cache_set() again using same key.

First of all let’s write a function that retrieve the templates we want to display.
There are a lot of ways we can set templates: option, configuration file, a filter, or a combination of them:

function get_custom_page_templates() {
  $templates = array();
  // maybe by options? --> $templates = get_option( 'custom_page_templates' );
  // maybe by conf file? --> $templates = include 'custom_page_templates.php';
  return apply_filters( 'custom_page_templates', $templates );
}

After that we need to retrieve the templates inside the page editing, just after WordPress saved the cache. Moreover we need to get them again when the editing form is posted to allow saving.

We can use 'edit_form_after_editor' hook for first scope, 'load-post.php' and 'load-post.new' for the second:

add_action( 'edit_form_after_editor', 'custom_page_templates_init' );
add_action( 'load-post.php', 'custom_page_templates_init_post' );
add_action( 'load-post-new.php', 'custom_page_templates_init_post' );

function custom_page_templates_init() {
  remove_action( current_filter(), __FUNCTION__ );
  if ( is_admin() && get_current_screen()->post_type === 'page' ) {
    $templates = get_custom_page_templates(); // the function above
    if ( ! empty( $templates ) )  {
      set_custom_page_templates( $templates );
    }
  }
}

function custom_page_templates_init_post() {
  remove_action( current_filter(), __FUNCTION__ );
  $method = filter_input( INPUT_SERVER, 'REQUEST_METHOD', FILTER_SANITIZE_STRING );
  if ( empty( $method ) || strtoupper( $method ) !== 'POST' ) return;
  if ( get_current_screen()->post_type === 'page' ) {
    custom_page_templates_init();
  }
}

Last thing we have to do, write the set_custom_page_templates() function that edit the cache to include our templates, being sure to merge any templates defined by file headers:

function set_custom_page_templates( $templates = array() ) {
  if ( ! is_array( $templates ) || empty( $templates ) ) return;
  $core = array_flip( (array) get_page_templates() ); // templates defined by file
  $data = array_filter( array_merge( $core, $templates ) );
  ksort( $data );
  $stylesheet = get_stylesheet();
  $hash = md5( get_theme_root( $stylesheet ) . "https://wordpress.stackexchange.com/" . $stylesheet );
  $persistently = apply_filters( 'wp_cache_themes_persistently', false, 'WP_Theme' );
  $exp = is_int( $persistently ) ? $persistently : 1800;
  wp_cache_set( 'page_templates-' . $hash, $data, 'themes', $exp );
}

Having this code running, you can setup custom templates just using the method you used in get_custom_page_templates() function, here I use the filter:

add_filter( 'custom_page_templates', function( $now_templates ) {

  $templates = array(
    'some-custom-page-template' => 'Some Custom Page Template',
    'another-custom-page-template' => 'Another Custom Page Template' ,
  );

  return array_merge( $now_templates, $templates );

} );

And you’re done.

Leave a Comment