I'm trying to dynamically add a rewrite rule that allows one to access any page under an alternate permalink structure of my choosing.
So for example, if the alternate permalink structure includes the slug foo/
in the first postion, I want to add a rewrite rule such that:
example.com/foo/my-page
rewrites to example.com/my-page
example.com/foo/my-cool/blog/post
rewrites to example.com/my-cool/blog/post
So basically, no matter whether the query is for a page, a post, or any other type of custom content, I want this content to be accessible under the /foo/
slug as well as the original non-foo URL structure.
I have written a working function that accomplishes this by..
- reading the URI
- removing the slug from he URI
- looping through all the existing rewrite rules
- matching the URI with the slug removed to an existing rewrite rule
- grabbing the matching groups for matched the rewrite rule
- compiling a new rewrite rule with the slugged URI as the source and the matched rewrite rule endpoint with matching groups replaced with their values as the destination.
Here is the code:
// functions.php
add_filter('rewrite_rules_array', function($rules) {
$slug = 'foo';
// get the request URI
$uri = $_SERVER['REQUEST_URI'];
// determine whether URI has slug in the first position
if(preg_match('/^(\/' . $slug . ')/', $uri)){
// get the base URI by removing the slug
$base_uri = str_replace('/'.$slug,'',$uri);
// loop through existing rewrite rules
foreach($rules as $src => $dest){
// find the rewrite rule for which the base URI would have matched
$regex_to_match = '/' . str_replace('/','\/',$src) . '/';
preg_match_all($regex_to_match, $base_uri, $matches, PREG_SET_ORDER);
if(count($matches) > 0){
// get the specific matching groups
$matches = $matches[0];
// compile valid regex from URI with slug to create new rewrite source
$new_src = ($uri[0] == '/' ? substr($uri, 1) : $uri) . '?$';
// replace match variables with their string values to create new rewrite destination
for($i=1; $i<count($matches)+1; $i++){
$replacement = isset($matches[$i]) ? $matches[$i] : '';
$dest = str_replace('$matches[' . $i . ']', $replacement, $dest);
}
$new_dest = $dest;
// add new rewrite rule to $wp_rewrite rules array
$rules[$new_src] = $new_dest;
// add the write rule
add_rewrite_rule($new_src,$new_dest,'top');
break;
}
}
}
return $rules;
});
flush_rewrite_rules();
This works but only for URLs with multiple slugs.
example.com/foo/my-page/123
rewrites correctly!
example.com/foo/abc/defg/bar
rewrites correctly!
example.com/foo/abc
DOES NOT rewrite correctly. Instead it redirects to example.com/abc
same for example.com/foo/my-page
-- it redirects to example.com/my-page