Commit da684075add for woocommerce
commit da684075add6f38dd94c9bf897bd2d16bf05568e
Author: Oleksandr Aratovskyi <79862886+oaratovskyi@users.noreply.github.com>
Date: Wed Sep 30 15:31:01 2026 +0300
Fix Product Categories List warnings when categories have no visible products (#69181)
* Fix missing parents in product category trees
* Add changelog entry for product category tree fix
* Add regression coverage for empty product category tree
* Narrow product category tree fix to the empty-root guard
Child categories whose parent is dropped for a stale count are skipped
again instead of promoted to the top level. Stale counts after
reparenting are tracked in #25491.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
diff --git a/plugins/woocommerce/changelog/fix-product-categories-missing-parent-tree b/plugins/woocommerce/changelog/fix-product-categories-missing-parent-tree
new file mode 100644
index 00000000000..f1419a06e26
--- /dev/null
+++ b/plugins/woocommerce/changelog/fix-product-categories-missing-parent-tree
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Fix PHP warnings in the Product Categories List block when listed categories only have hidden or out-of-stock products.
diff --git a/plugins/woocommerce/src/Blocks/BlockTypes/ProductCategories.php b/plugins/woocommerce/src/Blocks/BlockTypes/ProductCategories.php
index b2c3e6faed0..cf437e5440b 100644
--- a/plugins/woocommerce/src/Blocks/BlockTypes/ProductCategories.php
+++ b/plugins/woocommerce/src/Blocks/BlockTypes/ProductCategories.php
@@ -200,7 +200,7 @@ class ProductCategories extends AbstractDynamicBlock {
}
$parent_id = $children_only ? get_queried_object_id() : 0;
- $tree = $categories_by_parent[ 'cat-' . $parent_id ]; // these are top level categories. So all parents.
+ $tree = $categories_by_parent[ 'cat-' . $parent_id ] ?? []; // these are top level categories. So all parents.
unset( $categories_by_parent[ 'cat-' . $parent_id ] );
foreach ( $tree as $category ) {
diff --git a/plugins/woocommerce/tests/php/src/Blocks/BlockTypes/ProductCategoriesTest.php b/plugins/woocommerce/tests/php/src/Blocks/BlockTypes/ProductCategoriesTest.php
index e6d379a92b4..5c385a1ebcc 100644
--- a/plugins/woocommerce/tests/php/src/Blocks/BlockTypes/ProductCategoriesTest.php
+++ b/plugins/woocommerce/tests/php/src/Blocks/BlockTypes/ProductCategoriesTest.php
@@ -128,4 +128,30 @@ class ProductCategoriesTest extends WC_Unit_Test_Case {
$this->assertStringNotContainsString( '>Empty<', $this->render_product_categories() );
$this->assertStringContainsString( '>Empty<', $this->render_product_categories( '{"hasEmpty":true}' ) );
}
+
+ /**
+ * @testdox Should render nothing, without warnings, when the current category's children have no visible products.
+ * @testWith ["hidden", "instock", "no"]
+ * ["visible", "outofstock", "yes"]
+ *
+ * @param string $catalog_visibility Catalog visibility of the only product.
+ * @param string $stock_status Stock status of the only product.
+ * @param string $hide_out_of_stock Value of the woocommerce_hide_out_of_stock_items option.
+ */
+ public function test_children_only_without_visible_products( string $catalog_visibility, string $stock_status, string $hide_out_of_stock ): void {
+ update_option( 'woocommerce_hide_out_of_stock_items', $hide_out_of_stock );
+ $accessories_id = (int) wp_insert_term( 'Accessories', 'product_cat' )['term_id'];
+ $scarves_id = (int) wp_insert_term( 'Scarves', 'product_cat', array( 'parent' => $accessories_id ) )['term_id'];
+ WC_Helper_Product::create_simple_product(
+ true,
+ array(
+ 'category_ids' => array( $scarves_id ),
+ 'catalog_visibility' => $catalog_visibility,
+ 'stock_status' => $stock_status,
+ )
+ );
+ $this->go_to( get_term_link( $accessories_id, 'product_cat' ) );
+
+ $this->assertSame( '', $this->render_product_categories( '{"showChildrenOnly":true}' ) );
+ }
}