Commit d825abfef6 for woocommerce

commit d825abfef66ba5e3d7214f65e684a6fd37399625
Author: Lucio Giannotta <sunyatasattva@gmail.com>
Date:   Tue Jul 1 20:22:03 2025 +0200

    Fix brands shortcode being empty when `show_empty` was set to `false` (#59297)

    * Fix brands shortcodes being empty when `show_empty` was set to `false`

    This fixes a regression introduced in WooCommerce 9.9.4 where the
    `[product_brand_thumbnails show_empty="false"]` shortcode would return
    no results, even when brands with products existed.

    The issue was caused by PR #58797 which modified `wc_change_term_counts()`.
    This change resulted in brand terms having `$term->count = 0` even
    when they had associated products, due to how the product_brand taxonomy
    uses `_update_post_term_count` instead of WooCommerce’s `_wc_term_recount`.

    Removed the redundant `remove_terms_with_empty_products()` calls in both shortcode
    methods when `hide_empty` is true, since `get_terms()` already handles this filtering
    correctly.

    Fixes #59242

diff --git a/plugins/woocommerce/changelog/59297-wooplug-4823-woocommerce-brands-product_brand_thumbnails-shortcode-breaks b/plugins/woocommerce/changelog/59297-wooplug-4823-woocommerce-brands-product_brand_thumbnails-shortcode-breaks
new file mode 100644
index 0000000000..4502268ad1
--- /dev/null
+++ b/plugins/woocommerce/changelog/59297-wooplug-4823-woocommerce-brands-product_brand_thumbnails-shortcode-breaks
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Fixed brands shortcode being empty when `show_empty` was set to `false`.
\ No newline at end of file
diff --git a/plugins/woocommerce/includes/class-wc-brands.php b/plugins/woocommerce/includes/class-wc-brands.php
index 5846476590..1eede1fa38 100644
--- a/plugins/woocommerce/includes/class-wc-brands.php
+++ b/plugins/woocommerce/includes/class-wc-brands.php
@@ -567,13 +567,6 @@ class WC_Brands {
 		$alphabet       = apply_filters( 'woocommerce_brands_list_alphabet', range( 'a', 'z' ) );
 		$numbers        = apply_filters( 'woocommerce_brands_list_numbers', '0-9' );

-		/**
-		 * Check for empty brands and remove them from the list.
-		 */
-		if ( ! $show_empty_brands ) {
-			$terms = $this->remove_terms_with_empty_products( $terms );
-		}
-
 		foreach ( $terms as $term ) {
 			$term_letter = $this->get_brand_name_first_character( $term->name );

@@ -672,10 +665,6 @@ class WC_Brands {
 			return;
 		}

-		if ( $hide_empty ) {
-			$brands = $this->remove_terms_with_empty_products( $brands );
-		}
-
 		ob_start();

 		wc_get_template(
@@ -723,7 +712,7 @@ class WC_Brands {
 		$brands = get_terms(
 			'product_brand',
 			array(
-				'hide_empty' => $args['hide_empty'],
+				'hide_empty' => $hide_empty,
 				'orderby'    => $args['orderby'],
 				'exclude'    => $exclude,
 				'number'     => $args['number'],
@@ -735,10 +724,6 @@ class WC_Brands {
 			return;
 		}

-		if ( $hide_empty ) {
-			$brands = $this->remove_terms_with_empty_products( $brands );
-		}
-
 		ob_start();

 		wc_get_template(
@@ -1026,22 +1011,6 @@ class WC_Brands {
 		$product->save();
 	}

-	/**
-	 * Remove terms with empty products.
-	 *
-	 * @param WP_Term[] $terms The terms array that needs to be removed of empty products.
-	 *
-	 * @return WP_Term[]
-	 */
-	private function remove_terms_with_empty_products( $terms ) {
-		return array_filter(
-			$terms,
-			function ( $term ) {
-				return $term->count > 0;
-			}
-		);
-	}
-
 	/**
 	 * Invalidates the layered nav counts cache.
 	 *
diff --git a/plugins/woocommerce/tests/php/includes/class-wc-brands-test.php b/plugins/woocommerce/tests/php/includes/class-wc-brands-test.php
new file mode 100644
index 0000000000..1f1981645d
--- /dev/null
+++ b/plugins/woocommerce/tests/php/includes/class-wc-brands-test.php
@@ -0,0 +1,99 @@
+<?php
+/**
+ * WooCommerce Brands Unit tests suite
+ *
+ * @package woocommerce-brands
+ */
+
+declare( strict_types = 1);
+
+require_once WC_ABSPATH . '/includes/class-wc-brands.php';
+
+/**
+ * WC Brands test
+ */
+class WC_Brands_Test extends WC_Unit_Test_Case {
+	/**
+	 * Test that `product_brand_thumbnails` shortcode's `show_empty` argument works as expected.
+	 * This test prevents regression of the issue where double filtering caused no brands to be displayed.
+	 */
+	public function test_product_brand_thumbnails_shortcode_with_show_empty_arg() {
+		$data            = $this->setup_brand_test_data();
+		$brands_instance = $data['brands_instance'];
+
+		$output = $brands_instance->output_product_brand_thumbnails( array( 'show_empty' => 'false' ) );
+
+		// Full brand is shown, empty brand is not.
+		$this->assertStringContainsString( 'Full Brand', $output );
+		$this->assertStringNotContainsString( 'Empty Brand', $output );
+
+		$output = $brands_instance->output_product_brand_thumbnails( array( 'show_empty' => 'true' ) );
+
+		// Both brands are shown.
+		$this->assertStringContainsString( 'Full Brand', $output );
+		$this->assertStringContainsString( 'Empty Brand', $output );
+	}
+
+	/**
+	 * Test that `product_brand_thumbnails_description` shortcode's `show_empty` argument works as expected.
+	 */
+	public function test_product_brand_thumbnails_description_shortcode_with_show_empty_arg() {
+		$data            = $this->setup_brand_test_data();
+		$brands_instance = $data['brands_instance'];
+
+		$output = $brands_instance->output_product_brand_thumbnails_description( array( 'show_empty' => 'false' ) );
+
+		// Full brand is shown, empty brand is not.
+		$this->assertStringContainsString( 'Full Brand', $output );
+		$this->assertStringNotContainsString( 'Empty Brand', $output );
+
+		$output = $brands_instance->output_product_brand_thumbnails_description( array( 'show_empty' => 'true' ) );
+
+		// Both brands are shown.
+		$this->assertStringContainsString( 'Full Brand', $output );
+		$this->assertStringContainsString( 'Empty Brand', $output );
+	}
+
+	/**
+	 * Test that `product_brand_list` shortcode's `show_empty_brands` argument works as expected.
+	 */
+	public function test_product_brand_list_shortcode_with_show_empty_brands_arg() {
+		$data            = $this->setup_brand_test_data();
+		$brands_instance = $data['brands_instance'];
+
+		$output = $brands_instance->output_product_brand_list( array( 'show_empty_brands' => 'false' ) );
+
+		// Full brand is shown, empty brand is not.
+		$this->assertStringContainsString( 'Full Brand', $output );
+		$this->assertStringNotContainsString( 'Empty Brand', $output );
+
+		$output = $brands_instance->output_product_brand_list( array( 'show_empty_brands' => 'true' ) );
+
+		// Both brands are shown.
+		$this->assertStringContainsString( 'Full Brand', $output );
+		$this->assertStringContainsString( 'Empty Brand', $output );
+	}
+
+	/**
+	 * Helper method to set up test data for brand shortcode tests.
+	 *
+	 * @return array Contains brands instance, brand term IDs and product ID.
+	 */
+	private function setup_brand_test_data() {
+		WC_Brands::init_taxonomy();
+
+		$brand_with_products = wp_insert_term( 'Full Brand', 'product_brand' );
+		$empty_brand         = wp_insert_term( 'Empty Brand', 'product_brand' );
+
+		$product = WC_Helper_Product::create_simple_product();
+		$product->save();
+		wp_set_object_terms( $product->get_id(), array( $brand_with_products['term_id'] ), 'product_brand' );
+
+		return array(
+			'brands_instance'     => new WC_Brands(),
+			'brand_with_products' => $brand_with_products,
+			'empty_brand'         => $empty_brand,
+			'product'             => $product,
+		);
+	}
+}