Back to all posts

WP: Shortcode function to display product categories and subcategories in a sidebar


<?php

// Ensure the file is accessed within WordPress
defined('ABSPATH') || exit;

// Shortcode function to display product categories and subcategories in a sidebar
function product_categories_sidebar_shortcode()
{
    ob_start(); // Start output buffering

    // Get all product categories
    $args = array(
        'taxonomy'     => 'product_cat',
        'orderby'      => 'name',
        'order'        => 'ASC',
        'hide_empty'   => false,
    );

    $categories = get_terms($args);
    
    if (empty($categories)) {
        return '<p>No product categories found.</p>';
    }

    // Get the current term or product
    $current_term_id = 0;
    if (is_product()) {
        global $post;
        $terms = get_the_terms($post->ID, 'product_cat');
        if ($terms && !is_wp_error($terms)) {
            $current_term_id = $terms[0]->term_id; // Get the first category (or adjust as needed)
        }
    } else {
        $current_term = get_queried_object();
        $current_term_id = $current_term ? $current_term->term_id : 0;
    }

    // Get all parent IDs of the current term
    $parent_ids = get_all_parent_ids($current_term_id);
    // Include the current category in the active list
    if ($current_term_id) {
        $parent_ids[] = $current_term_id;
    }

    ?>
    <div class="product-categories-sidebar">
        <?php echo build_category_list($categories, $parent_ids); ?>
    </div>
    <?php

    return ob_get_clean(); // Return the buffered content
}

function build_category_list($categories, $parent_ids = array(), $parent_id = 0, $depth = 0)
{
    $output = '';
    $child_categories = array();

    foreach ($categories as $category) {
        if ($category->parent == $parent_id) {
            $child_categories[] = $category;
        }
    }

    if (!empty($child_categories)) {
        $output .= '<ul>';

        foreach ($child_categories as $category) {
            $is_active = (in_array($category->term_id, $parent_ids) || is_descendant_of($category->term_id, $parent_ids)) ? ' active' : '';
            $output .= '<li class="category-item' . esc_attr($is_active) . '" style="margin-left: ' . (20 * $depth) . 'px;">';
            $output .= '<a href="' . esc_url(get_term_link($category)) . '">';
            $output .= esc_html($category->name);
            $output .= '</a>';

            // Recursively show children
            $output .= build_category_list($categories, $parent_ids, $category->term_id, $depth + 1);

            $output .= '</li>';
        }

        $output .= '</ul>';
    }

    return $output;
}

function get_all_parent_ids($term_id) {
    $parent_ids = array();
    $current_term = get_term($term_id, 'product_cat');
    
    while ($current_term && $current_term->parent != 0) {
        $parent_ids[] = $current_term->parent;
        $current_term = get_term($current_term->parent, 'product_cat');
    }

    return array_reverse($parent_ids); // Optional: Reverse to get the hierarchy from top to bottom
}

function is_descendant_of($term_id, $parent_ids) {
    $descendants = get_term_children($term_id, 'product_cat');
    foreach ($descendants as $descendant_id) {
        if (in_array($descendant_id, $parent_ids)) {
            return true;
        }
    }
    return false;
}

add_shortcode('product_categories_sidebar', 'product_categories_sidebar_shortcode');

?>

Output: