You've already forked wc-bootstrap
Add product category tree sidebar to archive and single product pages (v0.1.7)
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:
@@ -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.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user