admin管理员组文章数量:1125799
I have a site which has pages that show products (these aren't sold on the site, though, it's just informational).
Some categories have subcategories, so the URI for a subcategory archive page looks like this:
/products/category-1/category-1-child/
But some categories don't have any subcategories, so the URI for a product post in that category looks like this:
/products/category-2/product-name/
So either the first URL works and the 2nd one is 404, or the second URL works and the first one is 404, as either a subcategory name or a post slug can be in that third position in the URL.
I suspect that the WP's built-in process which resolves permalinks to an internal URL which has the relevant information in query vars, seems to be unable to tell whether the URL points to a subcategory or a product post in a single-level category.
I've tried all sorts of settings using rewrite_rules_array
but I can only ever get one or the other working.
Here's what I have in the rewrite_rules_array
that works for posts but not subcategories, for example:
$new_rules['products/([^/]+)/([^/]+)/?$'] = 'index.php?product_category=$matches[1]&product=$matches[2]';
And here's a rule which works for a subcategory archive page, but fails on a single-level-category post:
$new_rules['products/([^/]+)/([^/]+)/?$'] = 'index.php?product_category=$matches[2]';
(Though this is also the default behaviour if I don't add anything.)
Note that the array key is the same for both URLs, as they both have the same URL format. I'm sure I'm missing something simple here!
I have a site which has pages that show products (these aren't sold on the site, though, it's just informational).
Some categories have subcategories, so the URI for a subcategory archive page looks like this:
/products/category-1/category-1-child/
But some categories don't have any subcategories, so the URI for a product post in that category looks like this:
/products/category-2/product-name/
So either the first URL works and the 2nd one is 404, or the second URL works and the first one is 404, as either a subcategory name or a post slug can be in that third position in the URL.
I suspect that the WP's built-in process which resolves permalinks to an internal URL which has the relevant information in query vars, seems to be unable to tell whether the URL points to a subcategory or a product post in a single-level category.
I've tried all sorts of settings using rewrite_rules_array
but I can only ever get one or the other working.
Here's what I have in the rewrite_rules_array
that works for posts but not subcategories, for example:
$new_rules['products/([^/]+)/([^/]+)/?$'] = 'index.php?product_category=$matches[1]&product=$matches[2]';
And here's a rule which works for a subcategory archive page, but fails on a single-level-category post:
$new_rules['products/([^/]+)/([^/]+)/?$'] = 'index.php?product_category=$matches[2]';
(Though this is also the default behaviour if I don't add anything.)
Note that the array key is the same for both URLs, as they both have the same URL format. I'm sure I'm missing something simple here!
Share Improve this question edited Jan 25, 2024 at 14:05 JoLoCo asked Jan 25, 2024 at 13:33 JoLoCoJoLoCo 1111 silver badge4 bronze badges 2- you mentioned the WP URL rewriter, what is this referring to? This is very ambiguous and could mean multiple things from a plugin to permalink issues to redirections to the wrong place. Can you state the problem in more concrete terms without referring to theories about the potential cause? E.g. I have URL A that should lead to product B but it instead goes to category C that has the same slug. Try to avoid implying things and state them explicitly even if it seems patronising – Tom J Nowell ♦ Commented Jan 25, 2024 at 13:53
- 1 Thanks, I'll update the question – JoLoCo Commented Jan 25, 2024 at 14:01
2 Answers
Reset to default 1Your challenge lies in the way WordPress interprets URL patterns and resolves them to query variables. When you have a URL structure where the third segment can be either a subcategory or a product name, WordPress struggles to differentiate between the two. This is a common issue in WordPress when using custom post types and taxonomies with similar permalink structures.
To resolve this, you need a strategy that helps WordPress distinguish between a subcategory and a product post. Here's a suggestion:
Modify the Rewrite Rules: You need to create rewrite rules that are specific enough to differentiate between a subcategory and a product. One way to do this is to add a prefix or a specific identifier in the URL for products.
Check for Existence of Subcategory: Another approach is to add a function in your functions.php file that checks if the given term exists as a subcategory. If it does, it redirects to the subcategory; otherwise, it assumes it's a product.
Here's an example of how you might implement this:
function wpb_custom_rewrite_rule() {
add_rewrite_rule('^products/([^/]+)/([^/]+)/?$', 'index.php?wpb_path=$matches[1]&wpb_second_path=$matches[2]', 'top');
}
add_action('init', 'wpb_custom_rewrite_rule', 10, 0);
function wpb_custom_query_vars($query_vars) {
$query_vars[] = 'wpb_path';
$query_vars[] = 'wpb_second_path';
return $query_vars;
}
add_filter('query_vars', 'wpb_custom_query_vars');
function wpb_template_redirect_intercept() {
global $wp_query;
if (isset($wp_query->query_vars['wpb_path']) && isset($wp_query->query_vars['wpb_second_path'])) {
$path = $wp_query->query_vars['wpb_path'];
$second_path = $wp_query->query_vars['wpb_second_path'];
// Check if $second_path is a subcategory
if (term_exists($second_path, 'product_category')) {
// It's a subcategory
$wp_query->set('product_category', $second_path);
} else {
// Assume it's a product
$wp_query->set('product', $second_path);
}
}
}
add_action('template_redirect', 'wpb_template_redirect_intercept');
In this code:
We add a new rewrite rule that captures two path segments after /products/. We add these segments to the query vars. In the template_redirect hook, we check if the second segment is a subcategory. If it is, we set the query to that subcategory. If not, we assume it's a product. Remember to flush your rewrite rules after adding this code (you can do this by visiting the Permalinks settings page in WordPress and clicking 'Save Changes').
This solution assumes that product_category and product are the correct query vars for your setup. Adjust them as needed based on your custom taxonomy and post type. Also, ensure your taxonomy and post type are set up to handle these custom query vars. ( make sure to flush your permalinks after changes like this )
The other answer (from 'WordPress Buddha') almost worked, in fact I suspect on a plain WP install it would work fine, but on my site it appeared that some other plugins were running too early to detect the change to the main WP Query, so some SEO and other stuff was showing the wrong information.
However, the answer did confirm to me that to keep this URL structure, we'd need to write some PHP code that would act differently depending on whether the slug in the 3rd 'part' of the URL was a product or a subcategory.
So after much experimentation, I ended up with this working code:
function wpse422073_handle_products_url_structure_clash () {
// We don't do anything here unless we have a URL which might be
// either a subcategory archive page, or a product in a top-level category,
// i.e. /products/top-category/slug-can-be-subcategory-or-product/
if ( str_starts_with( $_SERVER[ 'REQUEST_URI' ], '/products/' ) && mb_strlen( $_SERVER[ 'REQUEST_URI' ] ) > 10 ) {
// Get the parts of the URL into an array
// (with the slashes being the split points)
$uri_parts = explode( '/', trim( parse_url( $_SERVER[ 'REQUEST_URI' ], PHP_URL_PATH ), '/' ) );
// Again, we won't do anything if we don't have
// the right number of slashes with stuff between them
if ( count( $uri_parts ) === 3 ) {
// Check to see if the last slug in the URL is a product category
$category = term_exists( $uri_parts[ 2 ], 'product_category' );
if ( empty( $category ) ) {
// If the slug exists as a product category,
// then we'll treat this request as so
add_rewrite_rule( '^products/([^/]+)/([^/]+)/?$', 'index.php?product_category=$matches[1]&product=$matches[2]', 'top' );
}
else {
// If the slug isn't a product category, then it's a product page
add_rewrite_rule( '^products/([^/]+)/([^/]+)/?$', 'index.php?product_category=$matches[2]', 'top' );
}
// We have to call flush_rewrite_rules() to apply the changes,
// but we pass false to tell it to not update .htaccess (a "soft flush")
// although I've not actually seen it update .htaccess anyway,
// so perhaps there's some other thing we're missing there!
flush_rewrite_rules( false );
}
}
}
add_action( 'init', 'wpse422073_handle_products_url_structure_clash', 10, 0 );
So because this function is running on the init
hook, this does run on every non-cached page load, but we quickly do some string checking so the function exits fast on URLs that aren't connected to this issue. (I did string checking, but of course a regex could be used. The actual site has some more complications, not added here, which is why I went with string checking.)
If the URL matches the right format, we break the URL into sections (at the slashes) and check to see if the third element is a product category. If it is, then we set the rewrite rule for that. If it's not then we must have a product, so we set the rewrite rule for that.
Either way, we then flush the rewrite rules, which makes WordPress take note of the updated values. (Note that the rewrite key is the same for both, so they over-write each other.)
Doing a flush_rewrite_rules()
is described as "expensive" (i.e. slow) in the WP docs, but when testing I couldn't see any significant increase in the TTFB when doing this. (This may be because I'm adding false
to do a 'soft flush' which according to the docs doesn't update .htaccess
-- though I don't see the rewrites in .htaccess
either way, but that's another topic.)
And anyway, on this site all output is cached with .htaccess
rewriting (WP Super Cache's "expert mode" which means PHP and WP aren't even started if the requested page is in the cache already) so any slowdown would only affect the first request.
本文标签:
版权声明:本文标题:custom taxonomy - How to work with URLs where sometimes a post or a subcategory is in the same part of the URL structure 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1736672440a1946993.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论