Create A Virtual Page Using Rewrite Rules In WordPress
In our last tutorial we looked at how to create a endpoint. Those were pages that could be created for each type of permastruct available within WordPress. This time we are going to look at creating a page that does not rely on any of WordPress’ built in permastructs and instead can match any URL you like. I would advise reading the previous tutorial about Endpoints first, if you haven’t already, as this code extends the code used in that tutorial.
Github Repo
If you just want to grab the code & play around then head to Github and clone the repo. It includes the code from both tutorials as I used the same class to keep things simple (since they are related). Check out the readme in the repository for more information.
Adding The Rewrite Rule
Remember we are using our previous class for this tutorial so if you don’t already have the code this will make no sense. Either grab it from the Github repository or go follow the previous Endpoints tutorial first.
To add the rewrite rule is fairly simple. Here is the current code in our rewrite()
method.
1 2 3 4 5 6 7 8 |
function rewrite() { add_rewrite_endpoint( 'dump', EP_PERMALINK ); if(get_transient( 'vpt_flush' )) { delete_transient( 'vpt_flush' ); flush_rewrite_rules(); } } |
All we need to do is add an extra line after adding our endpoint. Like this.
1 2 3 4 5 6 7 8 9 |
function rewrite() { add_rewrite_endpoint( 'dump', EP_PERMALINK ); add_rewrite_rule( '^the-page$', 'index.php?vptutorial=1', 'top' ); if(get_transient( 'vpt_flush' )) { delete_transient( 'vpt_flush' ); flush_rewrite_rules(); } } |
What if you don’t want an endpoint too? Then remove the line adding the endpoint, or if you are using this tutorial as reference to write your own code, just don’t add it in.
Now you may be wondering how the function add_rewrite_rule()
works? It takes 3 parameters. The first is a regex pattern that explains what you’d like to match. Regex is tricky and it would take a whole new tutorial (in fact probably several) to explain how to write it. Instead the best idea is to search out a few tutorials and use a Regex Tester to make sure your pattern works before using it. Here I’m just using a simple text match, nothing complicated there. If you only want to match a specific slug then you should be fine doing the same.
The second parameter is the URL WordPress should actually fetch. This can be complicated to understand if you don’t know how WordPress’ pretty permalinks work, but to keep things simple let’s say it is where you instruct WordPress what page, post, archive (etc) you want to fetch when this URL is visited. The difference is you use a query string instead of the neat URLs. In this case we want to fetch our custom page so you can use any variable you like here with a 1 as the value. Just remember to keep it URL safe (no spaces, etc).
The final parameter is where in the rewrite rule stack should it be placed. Top will put it at the top and cause your rule to be matched before any of WordPress’ defaults when there is a conflict of rules. Bottom will put it at the bottom and will cause WordPress’ default rules to be matched over your rule if there is a conflict.
Query Variables
The variable we added in the second parameter of the add_rewrite_rule()
function is called a query variable & we must tell WordPress we have added it or it will cause problems later. To do this we need to add another action to our __construct()
in the class. Like this.
1 2 3 4 5 6 7 8 9 |
function __construct() { register_activation_hook( __FILE__, array( $this, 'activate' ) ); add_action( 'init', array( $this, 'rewrite' ) ); add_filter( 'query_vars', array( $this, 'query_vars' ) ); add_action( 'template_include', array( $this, 'change_template' ) ); } |
You can see that we have a new add_action()
call. This hooks the query_vars()
method, that we will create next, to run when the query_vars
action is called via WordPress.
The code for the query_vars()
method looks like this.
1 2 3 4 5 |
function query_vars($vars) { $vars[] = 'vptutorial'; return $vars; } |
We simple append the query variable we used in the rewrite rule and return it. Be warned. If you do not return the $vars
variable at the end of the method things will break pretty badly since it will not give all the normal query variables back to WordPress.
Adding Template Loading
Technically that’s all there is to it. Deactivate & reactivate the plugin, or save the permalinks page (to flush rewrite rules) and the page will be available. However because we aren’t telling WordPress what to do when that page is loaded it will just load the home page. So to fix that in your change_template()
method add a little code to load something special.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
function change_template( $template ) { if( get_query_var( 'dump', false ) !== false ) { //Check theme directory first $newTemplate = locate_template( array( 'template-dump.php' ) ); if( '' != $newTemplate ) return $newTemplate; //Check plugin directory next $newTemplate = plugin_dir_path( __FILE__ ) . 'templates/template-dump.php'; if( file_exists( $newTemplate ) ) return $newTemplate; } if( get_query_var( 'vptutorial', false ) !== false ) { $newTemplate = locate_template( array( 'template-vptutorial.php' ) ); if( '' != $newTemplate ) return $newTemplate; //Check plugin directory next $newTemplate = plugin_dir_path( __FILE__ ) . 'templates/template-vptutorial.php'; if( file_exists( $newTemplate ) ) return $newTemplate; } //Fall back to original template return $template; } |
Then create a template file, in the templates folder of your plugin folder or in your theme folder, called template-vptutorial.php
and place what you like in there. Remember to add get_header()
and get_footer()
if you want the rest of your theme to load. You can also use the example I placed in the Github Repository for this tutorial. Again for simplicities sake it is compatible with twentyfifteen.
Frequently Asked Questions
Can I change the name of the templates?
Of course. Name them whatever you like. Just remember to change the path/name in the change_template()
method.
Can I match a dynamic URL variable in the rewrite rule?
Yes. Just use a regex rule that matches what you’d like, for example ([0-9]+)
for a number and then use $matches[1]
to output the value in the second parameter. For example.
1 |
add_rewrite_rule( '^the-page/([0-9+]/?', 'index.php?vptutorial=$matches[1]', 'top' ); |
Note that the in the $matches
array the matches from the regex pattern start at 1 not 0.
That’s it. If you have any questions, problems or you notice a mistake please let me know in the comments & I’ll get back to you as soon as I can.
2 Comments
Patrick Johanneson
I think that the
add_action( 'query_vars', array( $this, 'query_vars' ) );
line should instead readadd_filter( 'query_vars', array( $this, 'query_vars' ) );
. According to the Developer Reference, it’s a filter hook, not an action hook.Paul Robinson
Hey Patrick,
Your right it is a filter. What is really weird is that it still works (I’ve just cloned the github repo which has the same typo). That being the case I’m not sure why that filter is needed. I always thought it was required to get WordPress to whitelist URL variables for use in URL rewriting.
I’ve changed it in the tutorial anyway and will push a change to the github repo to make sure anyone cloning that doesn’t have the same issue.
Thanks for pointing it out.