Display WooCommerce categories as accordion
When you have a WooCommerce shop and sell goods online, it can be useful to have a sidebar with a list of all product categories, including subcategories. The default built-in feature are widgets, and they have been based on blocks since WordPress 5.8. We can navigate to wp-admin / Appearance / Widgets and select “Product Categories List” or “Legacy Widget” → “Product Categories”. You can also configure widgets by applying options ”Show product counts” or “Show hierarchy”. The downsides of using default widgets are that you can’t control output markup and the fact that the configuration of the list is limited.
Custom WooCommerce Widget
We’re going to create a custom function that is querying product_cat terms from the database – WooCommerce accordion. The Markup of the list is based on Bootstrap5. If you’re enqueuing BS5 in your project, the list will already be styled. We will skip the default WP category “Uncategorized” by hardcoding term_id in the code.
/*
* WooCommerce Product List as accordion (BS5)
*/
function ct_render_cat_list()
{
global $wp_query;
$current_cat = $wp_query->get_queried_object();
$current_term_id = null;
$current_parent_id = isset($current_cat->term_id) ? $current_cat->term_id : null;
if(!is_null($current_parent_id)){
if($current_cat->parent != 0) {
$current_parent_id = $current_cat->parent;
}
$current_term_id = $current_cat->term_id;
}
/**
* Product categories list
*/
$args = array(
'taxonomy' => 'product_cat',
'hide_empty' => false,
'parent' => 0
);
$product_cat = get_terms($args);
echo '<h4>'. __('Categories') .'</h4>';
echo '<div class="accordion" id="accordionExample">';
foreach ($product_cat as $parent_product_cat) {
$parent_id = $parent_product_cat->term_id;
// skip 'Uncategorized'
if($parent_product_cat->term_id == '15'){
continue;
}
$button = '
<a class="accordion-button collapsed text-decoration-none" href="' . get_term_link($parent_product_cat->term_id) . '">
' . $parent_product_cat->name . ' ('. $parent_product_cat->count .')
</a>
';
if($parent_id == $current_parent_id) {
$button = '<button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#collapse'. $parent_id .'" aria-expanded="true" aria-controls="collapse' . $parent_id .'">
' . $parent_product_cat->name . ' ('. $parent_product_cat->count .')
</button>';
}
echo '
<div class="accordion-item">
<h2 class="accordion-header" id="headingOne">
' . $button .'
</h2>
';
if($parent_id == $current_parent_id) {
$child_args = array(
'taxonomy' => 'product_cat',
'hide_empty' => false,
'parent' => $parent_product_cat->term_id
);
$child_product_cats = get_terms($child_args);
if (!empty($child_product_cats)):
echo '<div id="collapse' . $parent_id . '" class="accordion-collapse collapse show" aria-labelledby="headingOne" data-bs-parent="#accordionExample">
<div class="accordion-body"> ';
echo "<ul class='navbar-nav me-auto mb-2 mb-md-0 '>";
foreach ($child_product_cats as $child_product_cat) {
$active_class = '';
if($current_term_id == $child_product_cat->term_id ) {
$active_class = 'active';
}
echo '<li class="nav-item"><a href="' . get_term_link($child_product_cat->term_id) . '" class="nav-link link-dark '. $active_class .'">' . $child_product_cat->name . ' ('. $child_product_cat->count .')</a></li>';
}
echo '</ul>';
echo '</div>
</div> <!-- / accordion-collapse -->';
endif;
}
echo '</div> <!-- accordion-item -->';
}
echo '</div> <!-- / accordion -->';
}
Define WordPress Widget
To have a WooCommerce Product List available on the Widgets list in the Admin Area, we need to define the proper PHP class that extends the WP_Widget class.
/**
* Define WordPress widget
*/
class CT_Cat_list_Widget extends WP_Widget
{
function __construct()
{
parent::__construct(
'CT_Cat_list_Widget',
esc_html__('Product categories2', 'text_domain'),
array('description' => esc_html__('Product categories as accordion', 'text_domain'),)
);
}
/**
* Front-end display of widget.
*
* @param array $args Widget arguments.
* @param array $instance Saved values from database.
* @see WP_Widget::widget()
*
*/
public function widget($args, $instance)
{
ct_render_cat_list();
}
}
function register_ct_widget() {
register_widget( 'CT_Cat_list_Widget' );
}
add_action( 'widgets_init', 'register_ct_widget' );
Register WP Shortcode
To use our ct_render_cat_list() function as page content, we can define the shortcode: [ct_render_cat_list] . We were using echo in the original function, but a proper shortcode needs returning the output. A simple solution will be to use ‘Output Buffering’ that will capture the HTML code and copy it to a variable.
/**
* Register WP shortcode
*/
add_shortcode( 'ct_render_cat_list', 'ct_render_cat_list_func' );
function ct_render_cat_list_func( $atts ) {
ob_start();
ct_render_cat_list();
return ob_get_clean();
}
Bootstrap5 styles
Our widget uses Bootstrap5 Accordion markup, we can enqueue BS5 styles from CDN using the wp_head action.
/**
* Enqueue Bootstrap CSS
*
*/
function ct_add_styles_to_head() {
echo '<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-KyZXEAg3QhqLMpG8r+8fhAXLRk2vvoC2f3B09zVXn8CA5QIVfZOJ3BCsw2P0p/We" crossorigin="anonymous">';
}
add_action('wp_head','ct_add_styles_to_head');
Additional improvements
An example would be to show how to query data from the database, prepare output compatible with Bootstrap v.5, or define the widget and shortcode in WordPress CMS. Currently, some texts / values are hardcoded in PHP code. The next step will be to add proper widget settings or shortcode arguments, for example: ‘List title’ or ‘ID of category to be skipped’.
We hope you find the information above useful. Make sure to follow us for other tips and guidelines.