Commit e8102bb3c0 for woocommerce
commit e8102bb3c0b05925aded573bb0e6566dff76c197
Author: Neil Carlo Sucuangco <necafasu@gmail.com>
Date: Tue Jan 20 17:44:35 2026 +0800
Fix duplicate breadcrumb entries on My Account endpoints (#62793)
* Fix duplicate breadcrumb entries on My Account endpoints
* Add changefile(s) from automation for the following project(s): woocommerce
* Update class-wc-breadcrumb-test.php
* Update class-wc-breadcrumb-test.php
* Fix phpstan errors
* Fix lint error
---------
Co-authored-by: woocommercebot <woocommercebot@users.noreply.github.com>
Co-authored-by: Alba Rincón <alba.rincon@automattic.com>
diff --git a/plugins/woocommerce/changelog/62793-fix-62760-duplicate-breadcrumb-my-account b/plugins/woocommerce/changelog/62793-fix-62760-duplicate-breadcrumb-my-account
new file mode 100644
index 0000000000..9b7563edda
--- /dev/null
+++ b/plugins/woocommerce/changelog/62793-fix-62760-duplicate-breadcrumb-my-account
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Fix duplicate breadcrumb entries on My Account endpoint pages when using the Breadcrumb block in global templates.
\ No newline at end of file
diff --git a/plugins/woocommerce/includes/class-wc-breadcrumb.php b/plugins/woocommerce/includes/class-wc-breadcrumb.php
index c53cbbee7c..570ca33aa4 100644
--- a/plugins/woocommerce/includes/class-wc-breadcrumb.php
+++ b/plugins/woocommerce/includes/class-wc-breadcrumb.php
@@ -204,7 +204,15 @@ class WC_Breadcrumb {
}
}
- $this->add_crumb( get_the_title(), get_permalink() );
+ // On WC endpoints, get_the_title() returns the endpoint title (via wc_page_endpoint_title filter).
+ // Use post_title directly to avoid duplicates like "Orders / Orders" instead of "My Account / Orders".
+ $permalink = get_permalink();
+ if ( is_wc_endpoint_url() ) {
+ $this->add_crumb( $post->post_title, $permalink ? $permalink : '' );
+ } else {
+ $this->add_crumb( get_the_title(), $permalink ? $permalink : '' );
+ }
+
$this->endpoint_trail();
}
diff --git a/plugins/woocommerce/phpstan-baseline.neon b/plugins/woocommerce/phpstan-baseline.neon
index 49a270e2f0..2814e85bf3 100644
--- a/plugins/woocommerce/phpstan-baseline.neon
+++ b/plugins/woocommerce/phpstan-baseline.neon
@@ -11739,7 +11739,7 @@ parameters:
-
message: '#^Parameter \#2 \$link of method WC_Breadcrumb\:\:add_crumb\(\) expects string, string\|false given\.$#'
identifier: argument.type
- count: 7
+ count: 6
path: includes/class-wc-breadcrumb.php
-
diff --git a/plugins/woocommerce/tests/php/includes/class-wc-breadcrumb-test.php b/plugins/woocommerce/tests/php/includes/class-wc-breadcrumb-test.php
new file mode 100644
index 0000000000..1dd68d5d58
--- /dev/null
+++ b/plugins/woocommerce/tests/php/includes/class-wc-breadcrumb-test.php
@@ -0,0 +1,218 @@
+<?php
+declare( strict_types = 1 );
+
+/**
+ * Tests for WC_Breadcrumb.
+ *
+ * @package WooCommerce\Tests\Includes
+ */
+
+/**
+ * WC_Breadcrumb_Test class.
+ */
+class WC_Breadcrumb_Test extends \WC_Unit_Test_Case {
+
+ /**
+ * The System Under Test.
+ *
+ * @var WC_Breadcrumb
+ */
+ private $sut;
+
+ /**
+ * Set up test fixtures.
+ */
+ public function setUp(): void {
+ parent::setUp();
+ $this->sut = new WC_Breadcrumb();
+ }
+
+ /**
+ * Tear down test fixtures.
+ */
+ public function tearDown(): void {
+ parent::tearDown();
+ $this->sut->reset();
+ global $wp_query, $post;
+ $wp_query = null; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
+ $post = null; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
+ }
+
+ /**
+ * @testdox My Account endpoint pages should show correct breadcrumb hierarchy without duplicates.
+ */
+ public function test_my_account_endpoint_breadcrumb_shows_correct_hierarchy() {
+ $my_account_page_id = wp_insert_post(
+ array(
+ 'post_type' => 'page',
+ 'post_status' => 'publish',
+ 'post_title' => 'My Account',
+ 'post_name' => 'my-account',
+ )
+ );
+ update_option( 'woocommerce_myaccount_page_id', $my_account_page_id );
+
+ global $post, $wp, $wp_query;
+ $post = get_post( $my_account_page_id ); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
+
+ $wp = new stdClass(); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
+ $wp->query_vars = array( 'orders' => '' );
+
+ $wp_query = new WP_Query(); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
+ $wp_query->is_page = true;
+ $wp_query->queried_object = $post;
+
+ $this->register_legacy_proxy_function_mocks(
+ array(
+ 'is_wc_endpoint_url' => function () {
+ return true;
+ },
+ 'is_page' => function () {
+ return true;
+ },
+ 'is_front_page' => function () {
+ return false;
+ },
+ )
+ );
+
+ $mock_query = $this->getMockBuilder( 'WC_Query' )
+ ->onlyMethods( array( 'get_current_endpoint', 'get_endpoint_title' ) )
+ ->getMock();
+ $mock_query->expects( $this->any() )
+ ->method( 'get_current_endpoint' )
+ ->willReturn( 'orders' );
+ $mock_query->expects( $this->any() )
+ ->method( 'get_endpoint_title' )
+ ->with( 'orders', '' )
+ ->willReturn( 'Orders' );
+
+ WC()->query = $mock_query;
+
+ $this->sut->add_crumb( 'Home', home_url() );
+ $this->sut->generate();
+
+ $breadcrumbs = $this->sut->get_breadcrumb();
+
+ $this->assertCount( 3, $breadcrumbs );
+ $this->assertEquals( 'Home', $breadcrumbs[0][0] );
+ $this->assertEquals( 'My Account', $breadcrumbs[1][0] );
+ $this->assertEquals( 'Orders', $breadcrumbs[2][0] );
+ $this->assertNotEquals( $breadcrumbs[1][0], $breadcrumbs[2][0] );
+
+ wp_delete_post( $my_account_page_id, true );
+ delete_option( 'woocommerce_myaccount_page_id' );
+ }
+
+ /**
+ * @testdox My Account edit-address endpoint should show correct breadcrumb hierarchy.
+ */
+ public function test_my_account_edit_address_endpoint_breadcrumb_shows_correct_hierarchy() {
+ $my_account_page_id = wp_insert_post(
+ array(
+ 'post_type' => 'page',
+ 'post_status' => 'publish',
+ 'post_title' => 'My Account',
+ 'post_name' => 'my-account',
+ )
+ );
+ update_option( 'woocommerce_myaccount_page_id', $my_account_page_id );
+
+ global $post, $wp, $wp_query;
+ $post = get_post( $my_account_page_id ); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
+
+ $wp = new stdClass(); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
+ $wp->query_vars = array( 'edit-address' => 'billing' );
+
+ $wp_query = new WP_Query(); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
+ $wp_query->is_page = true;
+ $wp_query->queried_object = $post;
+
+ $this->register_legacy_proxy_function_mocks(
+ array(
+ 'is_wc_endpoint_url' => function () {
+ return true;
+ },
+ 'is_page' => function () {
+ return true;
+ },
+ 'is_front_page' => function () {
+ return false;
+ },
+ )
+ );
+
+ $mock_query = $this->getMockBuilder( 'WC_Query' )
+ ->onlyMethods( array( 'get_current_endpoint', 'get_endpoint_title' ) )
+ ->getMock();
+ $mock_query->expects( $this->any() )
+ ->method( 'get_current_endpoint' )
+ ->willReturn( 'edit-address' );
+ $mock_query->expects( $this->any() )
+ ->method( 'get_endpoint_title' )
+ ->with( 'edit-address', '' )
+ ->willReturn( 'Addresses' );
+
+ WC()->query = $mock_query;
+
+ $this->sut->add_crumb( 'Home', home_url() );
+ $this->sut->generate();
+
+ $breadcrumbs = $this->sut->get_breadcrumb();
+
+ $this->assertCount( 3, $breadcrumbs );
+ $this->assertEquals( 'Home', $breadcrumbs[0][0] );
+ $this->assertEquals( 'My Account', $breadcrumbs[1][0] );
+ $this->assertEquals( 'Addresses', $breadcrumbs[2][0] );
+ $this->assertNotEquals( $breadcrumbs[1][0], $breadcrumbs[2][0] );
+
+ wp_delete_post( $my_account_page_id, true );
+ delete_option( 'woocommerce_myaccount_page_id' );
+ }
+
+ /**
+ * @testdox Regular pages without endpoints should work correctly.
+ */
+ public function test_regular_page_breadcrumb_works_correctly() {
+ $page_id = wp_insert_post(
+ array(
+ 'post_type' => 'page',
+ 'post_status' => 'publish',
+ 'post_title' => 'About Us',
+ 'post_name' => 'about-us',
+ )
+ );
+
+ global $post, $wp_query;
+ $post = get_post( $page_id ); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
+
+ $wp_query = new WP_Query(); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
+ $wp_query->is_page = true;
+ $wp_query->queried_object = $post;
+
+ $this->register_legacy_proxy_function_mocks(
+ array(
+ 'is_wc_endpoint_url' => function () {
+ return false;
+ },
+ 'is_page' => function () {
+ return true;
+ },
+ 'is_front_page' => function () {
+ return false;
+ },
+ )
+ );
+
+ $this->sut->add_crumb( 'Home', home_url() );
+ $this->sut->generate();
+
+ $breadcrumbs = $this->sut->get_breadcrumb();
+
+ $this->assertCount( 2, $breadcrumbs );
+ $this->assertEquals( 'Home', $breadcrumbs[0][0] );
+ $this->assertEquals( 'About Us', $breadcrumbs[1][0] );
+
+ wp_delete_post( $page_id, true );
+ }
+}