Take the hassle out of WordPress rewrite rules with an external library, Upstatement/routes for pain free routing and URL structures.
Looking for the solution? Jump straight to Upstatement/routes!
WordPress has grown a lot since its creation in 2003. Starting out as a simple blogging platform, it rapidly grew into the incredible software platform we know today.
Originally, WordPress’ permalink structure was rooted in the idea of categories, archives, and simple blog posts.
As the platform grew and diversified, accommodating everything from e-commerce sites to portfolios, the need for a more flexible and sophisticated routing system became evident. But it was still difficult to work around WordPress rewrite rules and their stubborn complexity.
In the very early days, WordPress URLs were straightforward. They often looked something like this:
http://example.com/?p=123
This structure mapped the URL in your browser directly to a database query.
For instance, ?p=123
here would be fetching the post with the ID of 123. Other parameters could be added, for example ?p=123&post_type=post&s=keyword
But this approach is very limited, and leaves room open for
As the platform matured, “pretty permalinks” were introduced, allowing URLs to be more human-readable and SEO-friendly, like so
http://example.com/2023/09/my-awesome-blog-post/
However, these pretty permalinks were still really limited. They were based on a set of predefined structures, primarily revolving around dates, post names, and categories. Customising them or adding in new URL structures became difficult. WordPress rewrite rules were still a limiting factor.
Imagine you’re setting up an online shop site with WordPress (forget that WooCommerce exists for a minute – let’s go back to basics!)
You want to run a promo where each product category has its own sale day:
http://example.com/sale/shoes/
http://example.com/sale/jackets/
Using traditional WordPress rewrite rules, achieving this structure would be a bit of a nightmare. You’d likely have to create a “sale” category and then add sub-categories like “shoes” and “jackets”. But what if “shoes” already exists as a primary category? The structure becomes messy and conflicts can arise.
Furthermore, what if you wanted to add a date to the sale, like:
http://example.com/sale/2023-09-16/shoes/
Using traditional WordPress permalinks, you’d face several challenges:
http://example.com/sale/2023-09-20/shoes
. Standard permalinks don’t easily allow for such dynamic date integrations within the URL.(If you’d like to see an example of the code required to map out something like this in WordPress, check out the end of the article)
To understand the power and importance of good permalink structures, let’s look at some examples from other sites:
As developers tried to get more control over their URLs with WordPress rewrite rules, particularly if they were digging into complex nested URL routes, the need for more advanced routing solutions arose.
During a project which required some particularly complicated routing config, I discovered a really cool project – https://github.com/Upstatement/routes
Upstatement/routes is designed for use with Timber, a flexible WordPress theme builder, but can be used as a standalone helper.
Upstatement allows for developers to bypass the WordPress rewrite rules and
This gives a lot more control over a project’s URL routes – especially if WordPress rewrite rules are giving you a headache, then installing Upstatement/routes could seriously help!
All you need to do is install it via Composer and ensure you’ve added require 'vendor/autoload.php';
to the top of your functions.php
composer require upstatement/routes
You’re then good to start implementing the routing functionality. Here’s a very basic example:
Routes::map('my-custom-page', function($params) {
$myvar = "Hello";
Routes::load('custom-template.php', ['myvar' => $myvar]);
});
This should then show you the contents of custom-template.php
(assuming it exists within your theme folder) when you visit /my-custom-page
in the browser.
Upstatement aims to replicate the way many modern PHP frameworks handle routing.
In the early days of the web, the browser’s URL path was directly mapped to the server’s file system, reflecting the exact files and folders on disk. This meant that the URL’s structure was often tied to the physical structure of the application’s files.
In time, URL rewriting became the norm, though there was still some connection between the URL and the physical disk or database structure. The default WordPress rewrite rules continued down that road, maintaining some degree of coupling between URL and posts/categories/archives.
However, modern frameworks have moved away from this approach. Instead, they use a style of routing that decouples the URL path from the filesystem. Frameworks such as Zend or Laravel deliver very clean and precise routing systems.
Imagine you’re the owner of a thriving e-commerce platform. As part of your marketing strategy, you decide to run a series of promotional events for different product categories. Each promo is unique, with some being time-sensitive, while others are evergreen.
Maybe you’ve already taken a look at WordPress rewrite rules syntax and found it lacking (and confusing!)
With tools like Upstatement’s router, we could build out our URL strategy like so
sale/:date/:category
. This allows for dynamic generation of URLs based on the date and category, making it easy to set up and manage multiple promos.sale/2023-09-20/shoes/
and the primary category shoes/
can coexist without issues.// easily define the URL path you want with a clean syntax
Routes::map('flash-sale/:category', function($params){
// some additional internal logic
$currentTime = time();
$saleStartTime = strtotime("today 2:00 PM");
$saleEndTime = strtotime("today 5:00 PM");
if ($currentTime < $saleStartTime || $currentTime > $saleEndTime) {
// If outside the flash sale window, redirect to a teaser page
wp_redirect(home_url('/flash-sale-teaser/'));
exit;
}
// specify the Loop query to use within the fallback template
$query = 'category_name=' . $params['category'] . '&orderby=rand&posts_per_page=5';
Routes::load('flash-sale.php', null, $query);
});
In this code,
Such dynamic and time-sensitive routes might be one way to significantly enhance user engagement. Customers might frequently check the site around potential flash sale times, increasing traffic and potential sales.
This is something that would be tough to throw together quickly using WordPress` rewrite rules out-of-the-box.
So you can see how this flexible, fluid approach lets you really open up the possibilities when it comes to WordPress routing and template design!
Here’s what the code to implement the e-commerce example URLs might look like with the default WordPress rewrite rules syntax. You can see how it’s
function custom_sale_rewrite_rule() {
// Add a rewrite rule for the sale structure
add_rewrite_rule(
'^sale/([0-9]{4}-[0-9]{2}-[0-9]{2})/([^/]+)/?$',
'index.php?sale_date=$matches[1]&sale_category=$matches[2]',
'top'
);
// Register custom query vars for the sale date and category
add_filter('query_vars', function($vars) {
$vars[] = 'sale_date';
$vars[] = 'sale_category';
return $vars;
});
}
add_action('init', 'custom_sale_rewrite_rule');
function handle_sale_request() {
if (get_query_var('sale_date') && get_query_var('sale_category')) {
// Fetch the products based on the sale date and category
// This would require additional logic to determine which products are on sale
$args = array(
'post_type' => 'product',
'tax_query' => array(
array(
'taxonomy' => 'product_cat',
'field' => 'slug',
'terms' => get_query_var('sale_category'),
),
),
);
$query = new WP_Query($args);
// Load a custom template to display the sale products
include 'path-to-your-sale-template.php';
exit;
}
}
add_action('template_redirect', 'handle_sale_request');
Robin is the dedicated developer behind Solarise.dev. With years of experience in web development, he's committed to delivering high-quality solutions and ensuring websites run smoothly. Always eager to learn and adapt to the ever-changing digital landscape, Robin believes in the power of collaboration and sharing knowledge. Outside of coding, he enjoys diving into tech news and exploring new tools in the industry.
If you'd like to get in touch to discuss a project, or if you'd just like to ask a question, fill in the form below.
Send me a message and I'll get back to you as soon as possible. Ask me about anything - web development, project proposals or just say hi!