0

I have some custom products that need to be loaded into a specific url structure, the code is the following:

foreach ($products as $prod) {
  $prod_obj = wc_get_product($prod->ID);
  
  $categories = wp_get_post_terms($prod->ID, 'product_cat');
  if (empty($categories) && is_wp_error($categories)) continue;
  
  $custom_slug = get_field('custom_slug', $prod->ID);

  add_rewrite_rule(
    "^cpo/watches/$custom_slug/?$",
    'index.php?post_type=product&p=' . $prod->ID . '&model=$matches[1]&size=$matches[2]&reference=$matches[3]',
    'top'
  );
}

the code above iterate over the $products and then add a rewrite rule that allow to show each product under a custom permalink using an acf field, so the final structure will look like this: https://example.com/cpo/watches/foo-456-hello-world.

Now the problem's that sometimes when I visit the url above I will get 404, so I have to flush the permalink in backend to make it working, and this is a pain. So a temporary workaround I did this:

add_action('template_redirect', function () {
  if (is_404()) {
    flush_rewrite_rules();
  }
});

The code above check if the current page is in 404 and then refresh the rules automatically, in this way if the page is refreshed the custom product is visible. Even though this solution "fix" the manual permalink refresh, I would like to avoid to show a 404 page to the final user, is there a way to keep the rewrite rules?

1 Answer 1

1

My usual trick to a) flush as required but b) not flush on every page load is to use an option to determine if the rewrites have changed, and flush only if they have indeed changed.

Something like this:

add_action( 'wp', 'wpse_421338_maybe_flush_rewrites' );
function wpse_421388_maybe_flush_rewrites() {
    $rewrite_obj = new WP_Rewrite();
    $rewrites    = $rewrite_obj->wp_rewrite_rules();
    // Compare the current state of the rewrites to the last stored state.
    $current_state = md5( json_encode( $rewrites ) );
    $stored_state  = get_option( 'wpse_421338_rewrite_state', '' );
    if ( $current_state !== $stored_state ) {
        // If things have changed, flush the rewrites...
        flush_rewrite_rules();
        // ...and store the new state.
        update_option( 'wpse_421338_rewrite_state', $current_state );
    }
}

...might work.

Note that this code is untested and should not be used in production until you're sure it works.

References

1
  • 1
    nice solution, thanks
    – sfarzoso
    Commented Dec 14, 2023 at 9:12

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.