Commit 55b7581b285 for woocommerce
commit 55b7581b285942a0beb45442fda1d39a4fd32a59
Author: Luigi Teschio <gigitux@gmail.com>
Date: Fri Jun 26 16:37:34 2026 +0200
Fix shop page permalink rewrite refresh (#65967)
* Fix shop page permalink rewrite refresh
* add E2E test
* fix E2E test
* improve logic
* improve logic
diff --git a/plugins/woocommerce/changelog/fix-shop-page-permalink-rewrite b/plugins/woocommerce/changelog/fix-shop-page-permalink-rewrite
new file mode 100644
index 00000000000..ea928dc232d
--- /dev/null
+++ b/plugins/woocommerce/changelog/fix-shop-page-permalink-rewrite
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Refresh product archive rewrite rules after the Shop page permalink changes.
diff --git a/plugins/woocommerce/includes/class-wc-post-types.php b/plugins/woocommerce/includes/class-wc-post-types.php
index dec6caf8e33..033007d79a3 100644
--- a/plugins/woocommerce/includes/class-wc-post-types.php
+++ b/plugins/woocommerce/includes/class-wc-post-types.php
@@ -30,6 +30,18 @@ class WC_Post_Types {
add_filter( 'rest_api_allowed_post_types', array( __CLASS__, 'rest_api_allowed_post_types' ) );
add_action( 'woocommerce_after_register_post_type', array( __CLASS__, 'maybe_flush_rewrite_rules' ) );
add_action( 'woocommerce_flush_rewrite_rules', array( __CLASS__, 'flush_rewrite_rules' ) );
+ // Similar logic exists in flush_rewrite_rules_on_shop_page_save(), but REST saves need a queued flush.
+ add_action(
+ 'save_post_page',
+ static function ( $post_id, $post, $update ) {
+ if ( ! $update ) {
+ return;
+ }
+ self::queue_rewrite_rules_on_shop_page_save( $post_id, $post );
+ },
+ 10,
+ 3
+ );
add_filter( 'gutenberg_can_edit_post_type', array( __CLASS__, 'gutenberg_can_edit_post_type' ), 10, 2 );
add_filter( 'use_block_editor_for_post_type', array( __CLASS__, 'gutenberg_can_edit_post_type' ), 10, 2 );
}
@@ -679,6 +691,33 @@ class WC_Post_Types {
}
}
+ /**
+ * Queue rewrite rules to be flushed after the shop page (or its ancestors) gets saved.
+ *
+ * @param int $post_id Post ID.
+ * @param WP_Post|null $post Post object.
+ */
+ private static function queue_rewrite_rules_on_shop_page_save( $post_id, $post = null ): void {
+ $post_id = absint( $post_id );
+
+ if ( ! $post_id || wp_is_post_revision( $post_id ) || wp_is_post_autosave( $post_id ) ) {
+ return;
+ }
+
+ $post_status = $post instanceof WP_Post ? $post->post_status : get_post_status( $post_id );
+
+ $shop_page_id = wc_get_page_id( 'shop' );
+
+ // Keep the existing behavior where a trashed shop page still serves the product archive at its previous URL.
+ if ( 0 >= $shop_page_id || 'trash' === $post_status ) {
+ return;
+ }
+
+ if ( $shop_page_id === $post_id || in_array( $post_id, get_post_ancestors( $shop_page_id ), true ) ) {
+ update_option( 'woocommerce_queue_flush_rewrite_rules', 'yes' );
+ }
+ }
+
/**
* Flush rewrite rules.
*/
diff --git a/plugins/woocommerce/tests/e2e/tests/blocks/templates/shop-page.block_theme.spec.ts b/plugins/woocommerce/tests/e2e/tests/blocks/templates/shop-page.block_theme.spec.ts
index d6093b2a16f..b80fff72738 100644
--- a/plugins/woocommerce/tests/e2e/tests/blocks/templates/shop-page.block_theme.spec.ts
+++ b/plugins/woocommerce/tests/e2e/tests/blocks/templates/shop-page.block_theme.spec.ts
@@ -1,21 +1,64 @@
/**
* External dependencies
*/
-import { test, expect, wpCLI } from '@woocommerce/e2e-utils';
+import type { Page } from '@playwright/test';
+import { test, expect, RequestUtils } from '@woocommerce/e2e-utils';
+
+const getShopPageId = async ( requestUtils: RequestUtils ) => {
+ const pages = await requestUtils.rest( {
+ path: '/wp/v2/pages?slug=shop',
+ } );
+ const shopPageId = pages[ 0 ]?.id;
+
+ if ( ! shopPageId ) {
+ throw new Error( 'Shop page ID not found' );
+ }
+
+ return shopPageId;
+};
+
+const expectShopTemplateToBeLoaded = async ( page: Page ) => {
+ await expect(
+ page.getByRole( 'heading', { name: 'Shop', level: 1 } )
+ ).toBeVisible();
+ await expect(
+ page.getByRole( 'heading', { name: 'Album' } ).first()
+ ).toBeVisible();
+};
test.describe( 'Shop page', () => {
test( 'template selector is not visible in the Page editor', async ( {
admin,
page,
+ requestUtils,
} ) => {
- const cliOutput = await wpCLI( 'option get woocommerce_shop_page_id' );
- const shopPageId = cliOutput.stdout.match( /\d+/ )?.pop();
- if ( ! shopPageId ) {
- throw new Error( 'Shop page ID not found' );
- }
+ const shopPageId = await getShopPageId( requestUtils );
- await admin.editPost( shopPageId );
+ await admin.editPost( String( shopPageId ) );
await expect( page.getByText( 'Template' ) ).toBeHidden();
} );
+
+ test( 'loads the product catalog template after the shop page permalink is updated via REST API', async ( {
+ page,
+ requestUtils,
+ } ) => {
+ const shopPageId = await getShopPageId( requestUtils );
+
+ await page.goto( 'shop/' );
+ await expectShopTemplateToBeLoaded( page );
+
+ const updatedShopPage = await requestUtils.rest( {
+ method: 'POST',
+ path: `/wp/v2/pages/${ shopPageId }`,
+ data: {
+ slug: 'market',
+ },
+ } );
+
+ expect( updatedShopPage.slug ).toBe( 'market' );
+
+ await page.goto( 'market/' );
+ await expectShopTemplateToBeLoaded( page );
+ } );
} );