admin管理员组文章数量:1122846
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/foo/my-page
rewrites to example/my-page
example/foo/my-cool/blog/post
rewrites to example/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/foo/my-page/123
rewrites correctly!
example/foo/abc/defg/bar
rewrites correctly!
example/foo/abc
DOES NOT rewrite correctly. Instead it redirects to example/abc
same for example/foo/my-page
-- it redirects to example/my-page
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
- I don't think you need to return anything since it's an action not a filter. (And the example here looks wrong to me too: if it were a filter you'd want to return $rewrite for the next filter anyway.) – Rup Commented Jan 17, 2022 at 12:01
- I'd start with the routing code in WP::parse_request(). There really isn't a lot to hook there though except maybe option_rewrite_rules, to filter the value WP_Rewrite::wp_rewrite_rules() reads from the database, and I don't like that. – Rup Commented Jan 17, 2022 at 12:07
1 Answer
Reset to default 0The issue with your updated code is that because you are using add_rewrite_rule()
inside the filter, it can't properly add it to the top of the list. You can see this if you dump out the $rules
array. Because it gets added to the end of the $rules
list, WP steps in beforehand to match on slug alone using some magic in the background.
In order to circumvent that process, you have to add it to the top of the list manually and return that from the filter.
Specifically, you need to change the add_rewrite_rule()
call to this:
$rules = [$new_src => $new_dest] + $rules;
I included the full updated code below.
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] : '';
$replacement = strpos($replacement, '/') === 0 ? substr($replacement, 1) : $replacement;
$dest = str_replace('$matches[' . $i . ']', $replacement, $dest);
}
$new_dest = $dest;
// add new rewrite rule to $wp_rewrite rules array
$rules = [$new_src => $new_dest] + $rules;
break;
}
}
}
return $rules;
});
This still feels like a weird way of doing this, and I'm sure there is a better one but without better understanding your use case I can't really provide an alternate suggestion. The biggest issue here to me is you are flushing the rewrite rules on every request, which can be an expensive operation.
Original Answer
I wouldn't hook into generate_rewrite_rules
for this. You can just use the regular rewrite rules API.
Tested the below, and it works for redirecting http://example.com/foo/test/123 to https://example.com/test/123. Can customize it to your needs. Drop it in functions.php or a functionality plugin.
add_action( 'init', function() {
add_rewrite_rule( 'foo/([a-z0-9-/]+)[/]?$', 'index.php?foo_redirect_to=$matches[1]', 'top' );
} );
add_filter( 'query_vars', function( $query_vars ) {
$query_vars[] = 'foo_redirect_to';
return $query_vars;
} );
add_action('template_redirect', function() {
if ( ( $redirect_to = get_query_var( 'foo_redirect_to' ) ) == false ) {
return;
}
wp_redirect("https://example.com/$redirect_to");
});
本文标签: plugin developmentAdding rewrite rule dynamically
版权声明:本文标题:plugin development - Adding rewrite rule dynamically 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1736292553a1928958.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论