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 );
+	} );
 } );