Add product category tree sidebar to archive and single product pages (v0.1.7)
All checks were successful
Create Release Package / PHP Lint (push) Successful in 1m4s
Create Release Package / PHPUnit Tests (push) Successful in 50s
Create Release Package / Build Release (push) Successful in 58s

Hierarchical category navigation with collapsible sub-levels up to 3 levels
deep, using Bootstrap 5 list-group and collapse components. Sidebar renders
on both archive/shop and single product pages with responsive offcanvas on
mobile. Active category highlighting and ancestor auto-expand for intuitive
navigation.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-29 15:19:53 +02:00
parent 5e4af247fa
commit 9860a184cd
9 changed files with 332 additions and 8 deletions

View File

@@ -293,6 +293,91 @@ function wc_bootstrap_loop_columns(): int {
}
add_filter( 'loop_shop_columns', 'wc_bootstrap_loop_columns' );
/**
* Build a hierarchical product category tree up to a given depth.
*
* Returns a nested array of category objects with children, suitable for
* rendering a multi-level navigation sidebar. Each node contains the
* WP_Term object plus a 'children' array and an 'is_active'/'is_ancestor'
* flag based on the current query.
*
* @since 0.1.7
*
* @param int $max_depth Maximum nesting depth (1 = top-level only, 3 = three levels).
* @return array Hierarchical category tree.
*/
function wc_bootstrap_get_category_tree( int $max_depth = 3 ): array {
$terms = get_terms( [
'taxonomy' => 'product_cat',
'hide_empty' => true,
'orderby' => 'name',
'order' => 'ASC',
] );
if ( is_wp_error( $terms ) || empty( $terms ) ) {
return [];
}
// Determine the currently viewed category (if any).
$current_term_id = 0;
$ancestor_ids = [];
if ( is_product_category() ) {
$queried = get_queried_object();
if ( $queried instanceof \WP_Term ) {
$current_term_id = $queried->term_id;
$ancestor_ids = get_ancestors( $current_term_id, 'product_cat', 'taxonomy' );
}
} elseif ( is_product() ) {
// On single product pages, highlight the first assigned category.
global $product;
if ( $product ) {
$cat_ids = $product->get_category_ids();
if ( ! empty( $cat_ids ) ) {
$current_term_id = $cat_ids[0];
$ancestor_ids = get_ancestors( $current_term_id, 'product_cat', 'taxonomy' );
}
}
}
// Index terms by parent for efficient tree building.
$by_parent = [];
foreach ( $terms as $term ) {
$by_parent[ $term->parent ][] = $term;
}
/**
* Recursively build tree nodes from the parent-indexed map.
*/
$build = function ( int $parent_id, int $depth ) use ( &$build, $by_parent, $max_depth, $current_term_id, $ancestor_ids ): array {
if ( $depth > $max_depth || ! isset( $by_parent[ $parent_id ] ) ) {
return [];
}
$nodes = [];
foreach ( $by_parent[ $parent_id ] as $term ) {
$children = $build( $term->term_id, $depth + 1 );
$is_active = ( $term->term_id === $current_term_id );
$is_ancestor = in_array( $term->term_id, $ancestor_ids, true );
$nodes[] = [
'term_id' => $term->term_id,
'name' => $term->name,
'slug' => $term->slug,
'count' => $term->count,
'url' => get_term_link( $term ),
'is_active' => $is_active,
'is_ancestor' => $is_ancestor,
'children' => $children,
];
}
return $nodes;
};
return $build( 0, 1 );
}
/**
* Remove WooCommerce's default sidebar hook.
*