add_rewrite_rule() driving me crazy, rewrite not working when analyzer says it should

I have a special situation and need some help achieving the final step.

Background:

When I access mydomain.com/category/postname, I get the post as expected.

When I access mydomain.com/category/postname?ajax=1, I get the post html (baked into and including its corresponding page template markup, which is extremely important for this use case), but I get it rendered without its header and footer (I did this on purpose, with a $_GET['ajax']==1 conditional inside the header.php and footer.php files to suppress them).

This all works as expected. However, it leaves me with a new small problem... Browsers won't properly cache content from URLs that end with a GET parameter string.

Therefore, I would like to create a rewrite rule that achieves the following...

mydomain.com/ajax/category/postname becomes mydomain.com/category/postname?ajax=1

Here's from my functions.php:

function dynaload_rewrite() {
  add_rewrite_rule('ajax/(.*)/?', '$matches[1]?ajax=1', 'bottom');
}
add_action('init', 'dynaload_rewrite');

This results in .htaccess file as follows:

# BEGIN WordPress
IfModule mod_rewrite.c
RewriteEngine On
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteRule ^ajax/(.*)/? /$matches[1]?ajax=1 [QSA,L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
/IfModule
# END WordPress

Expected Result: When I access /ajax/category/postname I want the content from /category/postname to render, minus the header and footer, as they do if I access normally with ?ajax=1

Actual Result: I am receiving the content from my WordPress 404 page, however the header and footer are omitted (so I know that the parameter ajax=1 is being passed correctly, it's just loading the wrong page).

Successful help is appreciated and will be deemed to be impressive. Thank you.

Topic rewrite-rules mod-rewrite Wordpress

Category Web


This works to capture the category and post_name then changes the template rendered.

if (!class_exists('WPTestPluginEndpoint')):

    class WPTestPluginEndpoint
    {
        const ENDPOINT_QUERY_NAME = 'ajax';

        // WordPress hooks

        public function init()
        {
            add_filter('query_vars', array($this, 'add_query_vars'), 0);
            add_action('parse_request', array($this, 'sniff_requests'), 0);
            add_action('init', array($this, 'add_endpoint'), 0);
        }

        // Add public query vars

        public function add_query_vars($vars)
        {
            $vars[] = 'ajax';
            $vars[] = 'cat';
            $vars[] = 'post_name';

            return $vars;
        }

        // Add API Endpoint

        public function add_endpoint()
        {
            add_rewrite_rule('^ajax/([^/]*)/([^/]*)/?', 'index.php?ajax=1&cat=$matches[1]&post_name=$matches[2]', 'top');

            flush_rewrite_rules(false); //// <---------- REMOVE THIS WHEN DONE
        }

        // Sniff Requests

        public function sniff_requests($wp_query)
        {
            global $wp;

            if (isset($wp->query_vars['ajax'])) {

                $_REQUEST ['ajax'] = $wp->query_vars['ajax'];
                $_REQUEST ['cat'] = $wp->query_vars['cat'];
                $_REQUEST ['post_name'] = $wp->query_vars['post_name'];

                $this->handle_request(); // handle it
            }
        }

        // Handle Requests

        protected function handle_request()
        {
            // Control the template used

            add_filter('template_include', function ($original_template) {

                $args = array(
                    'cat' => $_REQUEST['cat'],
                    'post_name' => $_REQUEST['post_name'],
                );

                //var_dump($original_template);

                return get_template_directory() . '/single.php';
            });
        }
    }

    $wptept = new WPTestPluginEndpoint();
    $wptept->init();

endif; // WPTestPluginEndpoint

In your template

if ( ! isset( $_REQUEST['ajax'] ) ) get_header(); ?>

<?php if ( ! isset( $_REQUEST['ajax'] ) ) get_footer(); ?>

Your external rule doesn't work because you're using a format that only works for internal rules- $matches[1] is essentially nonsense in the context of an .htaccess file. Using the $matches array will only work when rules are parsed by PHP, external rules need to use the standard $1 instead of $matches[1]. Any rule that doesn't point to index.php is interpreted as an external rule and gets added to .htaccess instead of the internal rules array.

That said, I suggest changing things a bit and adding a rewrite endpoint instead of a rule. Your URLs would be:

http://example.com/category/postname/ajax/

You could then check if get_query_var('ajax') is set in your templates.

About

Geeks Mental is a community that publishes articles and tutorials about Web, Android, Data Science, new techniques and Linux security.