Tell wordpress to show a single page instead of an archive

So I have multiple custom parameters in the url, and at some point when the query reaches 3 parameters (e.g. index.php?post_type=foo&<param-1>=<val-1>&<param-2>=<val-2>&<param-3>=<val-3>) there can be either one post or none. Instead of displaying archive (archive-foo.php) with this one post, I want wordpress to display the post itself (e.g. single-foo.php). Of course I can check all of this in the archive-foo.php and redirect to a corresponding post from there, but in this case I “waste” the whole query of displaying the archive.

So, is there any way to force wordpress load single page when archive contains only one record by manipulating the main query (using a custom function in functions.php and attaching it via add_action('pre_get_posts', '<func-name>'))?

Read More

Rough example:

add_action('pre_get_posts', 'custom_func')
function custom_func($query) {
    if($query->get('param-1')) {
        // Change some $query params, but still show archive
        if($query->get('param-2')) {
            // Change some $query params, but still show archive
            if($query->get('param-3')) {
                // There is 1 post or none for sure
                // Alter the query to something like
                $wpdb->query('SELECT * FROM wp_posts WHERE param1=val1 AND param2=val2 ...');
                // force to load a single page with the results passed to $post object
            }
        }
    }
}

Related posts

2 comments

  1. Here is a rough filter:

    add_filter(
      'template_include',
      function($template) {
        global $wp_query;
        if (1 == $wp_query->found_posts) {
          global $wp_query;
          $type = $wp_query->get('post_type') ?: false;
          $template_type = $type ? 'single-' . $type. '.php' : 'single.php';
          if ( locate_template($template_type) ) {
            return locate_template($template_type);
          } elseif ( $type && locate_template('single.php') ) {
            return locate_template('single.php');
          }
        }
        return $template;
      }
    );
    

    You will need to alter it so that it deals with custom single-{*}.php templates gracefully. (Edit by G.M.)

    I may edit the code a little later but I thought I’d get you started.

  2. I think you can do what using template_include() funciton:

     add_filter('template_include','alter_template');
     function alter_template($template){
    
         global $wp_query;
    
         if($wp_query->found_posts == 1) {
             $template = get_stylesheet_directory().'/single.php';
         }
    
         return $template;
      }
    

    Or if you want to redirect to the post, using template_direct():

    add_action( 'template_redirect', 'my_page_template_redirect' );
    function my_page_template_redirect()
    {
        global $wp_query;
        if($wp_query->found_posts == 1)
        {
            wp_redirect( 'URL_of_the_post );
            exit();
        }
    }
    

Comments are closed.