How to stop wordpress to make the default query?

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 ), "https://wordpress.stackexchange.com/" );
  $path = trim( substr( add_query_arg( array() ), strlen( $home_path ) ), "https://wordpress.stackexchange.com/" );
  $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( "https://wordpress.stackexchange.com/", $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.

Leave a Comment