Take the 2-minute tour ×
WordPress Development Stack Exchange is a question and answer site for WordPress developers and administrators. It's 100% free, no registration required.

Lets say i have a custom post type called 'event' that has several custom meta boxes for images, docs, etc..

Would it be possible to have two different single templates?

For example: i have a page that lists all events and if i click on an event, the single view shows me all the meta-info i put in event-single.php template.

now i want to have a different page that again lists all events, but if i click to view the single event, a different template is used, so i can show other meta-data connected with the event.

Or better:

page-events.php lists all events -> click on event -> shows single-event.php

page-media.php lists all events -> click on event -> shows single-event-whatever.php

Is this possible with WordPress?

share|improve this question
    
I you have a custom post type, 'event', why do you want to list all event in a single template instead of using the archive template? Aside of this, the answer to your question is YES, it is possible with Wordpress. –  cybmeta Sep 12 '13 at 9:09
    
Well, i'm not sure if my question was misleading: i absolutely do not list events in a single template. i just use a custom loop in a page-template and if i click on an event wordpress takes me to single-event.php. –  user1514045 Sep 12 '13 at 9:20
    
A page template is still a single-post template (a page is post type). I would use the archive template for the list of events. Using a page is making extra work: one query to determine which page to show, one more to show the list of events. Using the archive template you only run one query, the unncesary query to dtermine the page won't be execute. –  cybmeta Sep 12 '13 at 9:31
    
I'm sorry but I think your question meets the requirements of a "Work for me" format. –  cybmeta Sep 12 '13 at 14:24
    
Well, i was asking because with my limited knowledge of wordpress i couldn't come up with a good solution to my problem. And just because you had a bad day does not mean you have the right to insult me. I have more the feeling you haven't read my question cause your answer wasn't dealing with my problem at all. See G.M's answer, that deals with the problem i'm having. –  user1514045 Sep 16 '13 at 16:35

1 Answer 1

Your main problem is that when you list your events, you (most probably) use the the_permalink() function to get the url of the single event.

When this url is called, Wordpress show the single event, but how can Wordpress know if the link was clicked in the page-events.php or in the page-media.php?

If you don't say nothing to Wordpress (i.e. if you don't put information in the url) WP cannot distinguish if a link was clicked in a page or in other.

So, what I suggest you is to add an endpoint.

It means that you will have different url for your single event, e.g. if the standard link is something like http://example.com/events/one-event/ you can have also http://example.com/events/one-event/show/media or http://example.com/events/one-event/show/whatever.

show is the endpoint, it means that a variable show = media or show = whatever is passed as query var to Wordpess and so you can make use of it to include the wanted template.

To add the endpoint use:

add_action('init', 'add_show_endpoint');

function add_show_endpoint() {
  add_rewrite_endpoint( 'show', EP_PERMALINK );
}

Now when you are in the page-media.php you should have link like http://example.com/events/one-event/show/media and when you are in page-whatever.php you should have links like http://example.com/events/one-event/show/whatever.

How to accomplish this? It's simple: add a filter on get_permalink function.

add_action('wp_head', 'set_event_permalink_filter');

function set_event_permalink_filter() {
  $single_templates = array(
    // page template => endpoint to use
    'page-media.php' => 'media',
    'page-info.php' => 'info',
    'page-whatever.php' => 'whatever'
  );
  foreach ( $single_templates as $page => $endpoint) {
    if ( is_page_template($page) ) {
      global $event_endpoint;
      $event_endpoint = $endpoint;
      add_filter('post_link', 'event_permalink', 99, 3);
      return;
    }
  }
}

function event_permalink ($permalink, $post, $leavename) {
  if ( $post->post_type != 'events' ) return $permalink;
  global $event_endpoint;
  if ( empty($event_endpoint) ) return $permalink;
  return user_trailingslashit($permalink) . 'show/' . $event_endpoint . '/';
}

This workflow is a bit more complex than it can be, because the is_page_template() conditional tag doesn't work inside the loop (info), so we need to check page template before the loop start and use a global variable to pass the endpoint to the event_permalink function that is called by filter hook inside the loop.

At this point, if you go on the page, e.g. page-media.php all links generated for events post type end with /show/media/ and when you click on them the query variable show is setted to media.

So, now we can hook into template_include filter and based on the show variable include the right template. For the pourpose we can use locate_template to full support child themes.

add_filter('template_include', 'single_event_template');

function single_event_template( $template ) {
  if ( ! is_single('events') ) return $template;
  global $wp_query;
  $show = $wp_query->get('show');
  if ( empty($show) ) return $template;
  $file = locate_template( 'single-events-' . $show . '.php', true, false );
  return $file ? : $template;
}

So when we visit the link http://example.com/events/one-event/show/media (that is generated inside the page-media.php) if the template file single-event-media.php is found in parent or child theme it will be required, otherwise the standard single-event.php will be used.

Remember that after adding all the code in this answer to a plugin or in functions.php you have to flush the rewrite rules logging in your backend, going to Settings->Permalinks and clicking Save Changes.

Please note that code is totally untested and written here (with no syntax highlight), so it may contain typos and syntax errors: if you got some errors look in Codex (on php.net docs) for the code that trigger the error and check the right syntax.

In all the code is assumed your CPT is named 'events', change to fit your needs.

Also change the page templates to endpoint associations in the $single_templates variable inside set_event_permalink_filter function.


Plugin Code

I've put all above code together in a plugin that use a class (avoiding the use of the global variable.

There are 2 static variables in the class you have to change to fit your needs.

The plugin also contain a filter: 'multiple_cpt_single_templates' that allow adding page templates to endpoint associations from theme or from another plugin. You can even completely empty (set to array() ) the default $single_templates static variable and fill it via filter.

Being a plugin, I use the register_activation_hook to flush rewrite rules.

<?php
/**
 * Plugin Name: Multiple CPT Single Templates
 * Plugin URI: http://wordpress.stackexchange.com/questions/113878/multiple-single-templates
 * Author: G. M.
 * Author URI: http://wordpress.stackexchange.com/users/35541/
 */

add_action('after_setup_theme', 'initMultipleCptSingleTemplates');

function initMultipleCptSingleTemplates() {
    MultipleCptSingleTemplates::init();
}

register_activation_hook( __FILE__, array('MultipleCptSingleTemplates', 'install') );
register_deactivation_hook( __FILE__, array('MultipleCptSingleTemplates', 'uninstall') );

class MultipleCptSingleTemplates {

  /**
  * CHANGES FOLLOWING TWO VARIABLES ACCORDING TO YOUR NEEDS
  */

  static $cpt = 'events';

  static $single_templates =  array(
    // page template => endpoint to use
    'page-media.php' => 'media',
    'page-info.php' => 'info',
    'page-whatever.php' => 'whatever'
  );

  /** CONFIGIGURATION END */


  static $endpoint;

  static function init() {
    add_action('init', array( __CLASS__, 'add_show_endpoint') );
    add_filter('template_include', array( __CLASS__, 'single_event_template') );
    add_action('wp_head', array( __CLASS__, 'set_event_permalink_filter') );
  }

  static function add_show_endpoint() {
    add_rewrite_endpoint( 'show', EP_PERMALINK );
  }

  static function set_event_permalink_filter() {
    $single_templates = apply_filter('multiple_cpt_single_templates', self::$single_templates);
    if ( empty($single_templates) ) return;
    foreach ( $single_templates as $page => $endpoint) {
      if ( is_page_template($page) ) {
        self::$endpoint = $endpoint;
        add_filter('post_link', array( __CLASS__, 'event_permalink'), 99, 3);
        return;
      }
    }
  }

  static function event_permalink ($permalink, $post, $leavename) {
    if ( $post->post_type != self::$cpt ) return $permalink;
    if ( empty(self::$endpoint) ) return $permalink;
    return user_trailingslashit($permalink) . 'show/' . self::$endpoint . '/';
  }

  static function event_permalink ($permalink, $post, $leavename) {
    if ( $post->post_type != self::$cpt ) return $permalink;
    if ( empty(self::$endpoint) ) return $permalink;
    return user_trailingslashit($permalink) . 'show/' . self::$endpoint . '/';
  }

  static  function install( $template ) {
    self::add_show_endpoint();
    flush_rewrite_rules();
  }

  static  function uninstall( $template ) {
    flush_rewrite_rules();
  }

}
share|improve this answer
    
Many many thanks for this, @G.M.! As a workaround to my problem i used a combination of add_query_arg in page-media.php and get_template_part in single-event.php. Nonetheless i try to incorporate your code in my project and update the answer/solution accordingly! Again, many thanks for this solution, this looks a lot better than my somewhat hackish workaround. –  user1514045 Sep 17 '13 at 7:38

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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