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.

My goal is a redirect to a custom template based on some new rewrite rule, but without having wordpress to make unusefull queries.

my code:

function add_rewrite_rules() {

    add_rewrite_rule(
        '^settore/([^/]*)/?$',
        'index.php?settore=$matches[1]',
        'top'
    );

  add_rewrite_tag('%settore%','(caldareria|tessile)');
}
add_action( 'init', 'add_rewrite_rules' );

function set_root_category_template($template){
    $settore = get_query_var('settore');
    if($settore != '') $template = locate_template('root-taxonomy.php');
    return $template;
}
add_filter('template_include','set_root_category_template',99);

It redirects just right, but if I check $wp_query->posts in root-taxonomy.php, I see a list of regular post. Instead, in that page I'll make a custom query asking for some specific taxonomy terms data, and therefore, the default query is totally useless. How can I stop it? After all, I'm not passing any default query var to index.php so I can't understand the reason why wp is still doing a query..

share|improve this question
    
template_redirect hook instead of template_include would be a good start –  JMau Jun 13 at 9:37
    
but docs say that it's not a good idea for loading different templates [codex.wordpress.org/Plugin_API/Action_Reference/… –  Stratboy Jun 13 at 9:49
    
If you are still doing something reasonably conventional (querying posts) you should adjust main query instead of discarding it. Getting rid of it completely is impractical and messy. –  Rarst Jun 13 at 11:12
    
when both template_include and template_redirect are fired main query already happen, so using any of the two hooks you can't stop it. –  G. M. Jun 13 at 11:13
    
@Rarst "impractical" is probably a good definition for my answer :D –  G. M. Jun 13 at 11:14

1 Answer 1

up vote 3 down vote accepted

Stop WordPress to run main query is hard: you have to dig inside the deep heart of WordPress.

WordPress frontend workflow:

  1. wp-blog-header.php call the function wp()
  2. wp() function creates an instance of wp class, and call main() method on it
  3. main() method code:

    public function main($query_args = '') {
      $this->init();
      $this->parse_request($query_args); // build query vars starting from url
      $this->send_headers();
      $this->query_posts();  // run main query via WP_Query using built query vars
      $this->handle_404(); // if query has no results set 404 headers and WP_Query props
      $this->register_globals();
    }
    
  4. wp-blog-header.php include template-loader.php that load template file based on query

So only way to stop main query is prevent wp() to run.

That can be done hooking an early hook, do what you need, and finally exit().

The problem of this approach is that preventing wp() to run, you also prevent wp class to parse request, so the rewrite stuff you add are not parsed.

So, instead of to add a rewrite tag and a rewrite rule, you may want to look straight at the url to choose if it is the one that should trigger your custom action.

First of all let's write a function that return only the relative parte of the url, e.g. if current url is something like example.com/wp/path/to/somewhere?foo=bar and your home url is example.com/wp/ the function returns path/to/somewhere :

function get_relative_url() {
  $home_path = rtrim( parse_url( home_url(), PHP_URL_PATH ), '/' );
  $path = trim( substr( add_query_arg( array() ), strlen( $home_path ) ), '/' );
  $qs = array_keys( $_GET );
  if ( ! empty( $qs ) ) {
    $path = remove_query_arg( $qs, $path );
  }
  return $path;
}

Now we can use an early hook to look at the url, and only if it contains the desired path we'll set the $settore variable, load the template and exit:

add_action( 'wp_loaded', function() { // 'wp_loaded' hook happen before wp() is ran

  $path = get_relative_url();
  $url_parts = explode( '/', $path );
  if (
    isset( $url_parts[1] )
    && $url_parts[0] === 'settore'
    && in_array( $url_parts[1], array( 'caldareria', 'tessile' ), TRUE )
  ) {
    // ok the current url is something like example.com/settore/tessile
    // and 'tessile' is in the variable $url_parts[1]
    $template = locate_template( 'root-taxonomy.php' );
    if ( empty( $template ) ) return; // do nothing if template not found
    global $settore;
    $settore = $url_parts[1]; // in the template you can access to $settore variable.
    require_once $template;
    exit(); // prevent WordPress to do anything else
  }

} );

That's all.

In code above, understanding if the current url is the right one, and set the $settore variable was easy because the url structure was easy, for more complex url structures, you probably need a regex matching, and maybe use a tool like Symfony routing component to match the url to some variables.

This approach is the one I've used in Cortex, have a look there if you plan to commonly do thing like that.

Gotchas

as you can see, the main query (global $wp_query) in code I posted is not used, in your template it will be an empty WP_Query object, so anything will refer to main query, e.g. template tags like is_tax(), is_archive() or such, will not work.

That will apply to header and footer as well, because very probably in your 'root-taxonomy.php' you'll use get_header() and get_footer().

Also some plugins may not work properly, e.g. if you are using any SEO plugin that set title tag based on the query, than it will have issues.

A possible solution is to manually set main query properties accessing to global $wp_query or use a custom header, e.g. get_header('root-tax'), for your custom template and handle in header-root-tax.php custom title tag and/or any other issue.

share|improve this answer
    
Yes this can be usefull, thank you! And great job with the Brain project! –  Stratboy Jun 13 at 12:04
    
P.s. you should show your project to this scientist ;) –  Stratboy Jun 13 at 12:07
    
@Stratboy yep, or maybe to him :) –  G. M. Jun 13 at 12:17

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.