feat: migrate from HashRouter to BrowserRouter for SEO
Phase 1: WordPress Rewrite Rules - Add rewrite rule for /store/* to serve SPA page - Add use_browser_router setting toggle (default: true) - Flush rewrite rules on settings change Phase 2: React Router Migration - Add BrowserRouter with basename from WordPress config - Pass basePath and useBrowserRouter to frontend - Conditional router based on setting Phase 3: Hash Route Migration - Update EmailManager.php reset password URL - Update EmailRenderer.php login URL - Update TemplateOverride.php WC redirects - All routes now use path format by default This enables proper SEO indexing as search engines can now crawl individual product/page URLs.
This commit is contained in:
@@ -194,18 +194,29 @@ class Assets {
|
||||
];
|
||||
}
|
||||
|
||||
// Determine SPA base path for BrowserRouter
|
||||
$spa_page_id = $appearance_settings['general']['spa_page'] ?? 0;
|
||||
$spa_page = $spa_page_id ? get_post($spa_page_id) : null;
|
||||
$base_path = $spa_page ? '/' . $spa_page->post_name : '/store';
|
||||
|
||||
// Check if BrowserRouter is enabled (default: true for SEO)
|
||||
$use_browser_router = $appearance_settings['general']['use_browser_router'] ?? true;
|
||||
|
||||
$config = [
|
||||
'apiUrl' => rest_url('woonoow/v1'),
|
||||
'apiRoot' => rest_url('woonoow/v1'),
|
||||
'nonce' => wp_create_nonce('wp_rest'),
|
||||
'siteUrl' => get_site_url(),
|
||||
'siteTitle' => get_bloginfo('name'),
|
||||
'siteName' => get_bloginfo('name'),
|
||||
'storeName' => get_bloginfo('name'),
|
||||
'storeLogo' => $logo_url,
|
||||
'user' => $user_data,
|
||||
'theme' => $theme_settings,
|
||||
'currency' => $currency_settings,
|
||||
'appearanceSettings' => $appearance_settings,
|
||||
'basePath' => $base_path,
|
||||
'useBrowserRouter' => $use_browser_router,
|
||||
];
|
||||
|
||||
?>
|
||||
|
||||
@@ -13,6 +13,14 @@ class TemplateOverride
|
||||
*/
|
||||
public static function init()
|
||||
{
|
||||
// Register rewrite rules for BrowserRouter SEO (must be on 'init')
|
||||
add_action('init', [__CLASS__, 'register_spa_rewrite_rules']);
|
||||
|
||||
// Flush rewrite rules when appearance settings are updated
|
||||
add_action('update_option_woonoow_appearance_settings', function() {
|
||||
flush_rewrite_rules();
|
||||
});
|
||||
|
||||
// Redirect WooCommerce pages to SPA routes early (before template loads)
|
||||
add_action('template_redirect', [__CLASS__, 'redirect_wc_pages_to_spa'], 5);
|
||||
|
||||
@@ -46,6 +54,44 @@ class TemplateOverride
|
||||
add_action('get_header', [__CLASS__, 'remove_theme_header']);
|
||||
add_action('get_footer', [__CLASS__, 'remove_theme_footer']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register rewrite rules for BrowserRouter SEO
|
||||
* Catches all /store/* routes and serves the SPA page
|
||||
*/
|
||||
public static function register_spa_rewrite_rules()
|
||||
{
|
||||
$appearance_settings = get_option('woonoow_appearance_settings', []);
|
||||
$spa_page_id = $appearance_settings['general']['spa_page'] ?? 0;
|
||||
|
||||
// Check if BrowserRouter is enabled (default: true for new installs)
|
||||
$use_browser_router = $appearance_settings['general']['use_browser_router'] ?? true;
|
||||
|
||||
if (!$spa_page_id || !$use_browser_router) {
|
||||
return;
|
||||
}
|
||||
|
||||
$spa_page = get_post($spa_page_id);
|
||||
if (!$spa_page) {
|
||||
return;
|
||||
}
|
||||
|
||||
$spa_slug = $spa_page->post_name;
|
||||
|
||||
// Rewrite /store/anything to serve the SPA page
|
||||
// React Router handles the path after that
|
||||
add_rewrite_rule(
|
||||
'^' . preg_quote($spa_slug, '/') . '/(.*)$',
|
||||
'index.php?page_id=' . $spa_page_id . '&woonoow_spa_path=$matches[1]',
|
||||
'top'
|
||||
);
|
||||
|
||||
// Register query var for the SPA path
|
||||
add_filter('query_vars', function($vars) {
|
||||
$vars[] = 'woonoow_spa_path';
|
||||
return $vars;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Intercept add-to-cart redirect (NOT the add-to-cart itself)
|
||||
@@ -98,13 +144,14 @@ class TemplateOverride
|
||||
|
||||
/**
|
||||
* Redirect WooCommerce pages to SPA routes
|
||||
* Maps: /shop → /store/#/, /cart → /store/#/cart, etc.
|
||||
* Maps: /shop → /store/, /cart → /store/cart, etc.
|
||||
*/
|
||||
public static function redirect_wc_pages_to_spa()
|
||||
{
|
||||
// Get SPA page URL
|
||||
$appearance_settings = get_option('woonoow_appearance_settings', []);
|
||||
$spa_page_id = $appearance_settings['general']['spa_page'] ?? 0;
|
||||
$use_browser_router = $appearance_settings['general']['use_browser_router'] ?? true;
|
||||
|
||||
if (!$spa_page_id) {
|
||||
return; // No SPA page configured
|
||||
@@ -118,9 +165,19 @@ class TemplateOverride
|
||||
|
||||
$spa_url = trailingslashit(get_permalink($spa_page_id));
|
||||
|
||||
// Helper function to build route URL based on router type
|
||||
$build_route = function($path) use ($spa_url, $use_browser_router) {
|
||||
if ($use_browser_router) {
|
||||
// Path format: /store/cart
|
||||
return $spa_url . ltrim($path, '/');
|
||||
}
|
||||
// Hash format: /store/#/cart
|
||||
return rtrim($spa_url, '/') . '#/' . ltrim($path, '/');
|
||||
};
|
||||
|
||||
// Check which WC page we're on and redirect
|
||||
if (is_shop()) {
|
||||
wp_redirect($spa_url . '#/', 302);
|
||||
wp_redirect($build_route('shop'), 302);
|
||||
exit;
|
||||
}
|
||||
|
||||
@@ -128,23 +185,23 @@ class TemplateOverride
|
||||
global $product;
|
||||
if ($product) {
|
||||
$slug = $product->get_slug();
|
||||
wp_redirect($spa_url . '#/products/' . $slug, 302);
|
||||
wp_redirect($build_route('product/' . $slug), 302);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_cart()) {
|
||||
wp_redirect($spa_url . '#/cart', 302);
|
||||
wp_redirect($build_route('cart'), 302);
|
||||
exit;
|
||||
}
|
||||
|
||||
if (is_checkout() && !is_order_received_page()) {
|
||||
wp_redirect($spa_url . '#/checkout', 302);
|
||||
wp_redirect($build_route('checkout'), 302);
|
||||
exit;
|
||||
}
|
||||
|
||||
if (is_account_page()) {
|
||||
wp_redirect($spa_url . '#/account', 302);
|
||||
wp_redirect($build_route('my-account'), 302);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user