How to work with URLs where sometimes a post or a subcategory is in the same part of the URL structure

Your 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 )