admin管理员组

文章数量:1122826

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..

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 asked Jun 13, 2014 at 9:35 Luca ReghellinLuca Reghellin 1,6402 gold badges21 silver badges45 bronze badges 6
  • template_redirect hook instead of template_include would be a good start – JMau Commented Jun 13, 2014 at 9:37
  • but docs say that it's not a good idea for loading different templates [codex.wordpress.org/Plugin_API/Action_Reference/… – Luca Reghellin Commented Jun 13, 2014 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 Commented Jun 13, 2014 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. – gmazzap Commented Jun 13, 2014 at 11:13
  • @Rarst "impractical" is probably a good definition for my answer :D – gmazzap Commented Jun 13, 2014 at 11:14
 |  Show 1 more comment

3 Answers 3

Reset to default 6

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.

why not like this?

add_action( 'pre_get_posts', 'my_post_queries' );
function my_post_queries( $query ) {
    if ($query->is_main_query() && !is_admin()) {
        // maybe check for specific page here
        // if (is_tax('your-taxonomy') { 
        $query = false;
        remove_all_actions ( '__after_loop');
        // }
    }
}

Or did I misunderstand the question?

Here's what worked for me to avoid unnecessarily running the main query. Place this in your theme's functions.php file:

add_filter( 'posts_pre_query', 'prevent_main_query_from_running', 10, 2 );
function prevent_main_query_from_running( $posts, WP_Query $Query ) {
    if ( $Query->is_main_query() && ! is_admin() ) {
        add_filter( 'pre_handle_404', function( $bool, WP_Query $Query ) {
            return true;
        }, 10, 2 );
        return array();
    }
    return $posts;
}

See this YouTube video for deep explanation.

Note: this will apply to all front end pages, so unless that's what you're trying to accomplish, I would recommend making it more specific, maybe exclusively on a single page you're trying to avoid loading the main query on. For example, to prevent the main query from loading on the home page only, you can change that third line above to

if ( $Query->is_main_query() && $Query->is_home() && ! is_admin() ) {

本文标签: url rewritingHow to stop wordpress to make the default query