5 ) { $columns = 3; } // Validate aspect ratio. $allowed_aspect_ratios = array( '1/1', '3/4', '4/3', '16/9' ); if ( ! in_array( $image_aspect_ratio, $allowed_aspect_ratios, true ) ) { $image_aspect_ratio = '3/4'; } // --------------------------------------------------------------------------- // Query categories based on the selected source mode. // --------------------------------------------------------------------------- if ( 'manual' === $category_source ) { // Build a list of sanitised category IDs in user-defined order. $selected_ids = array(); $featured_map = array(); foreach ( $selected_categories as $item ) { if ( ! isset( $item['id'] ) ) { continue; } $id = absint( $item['id'] ); if ( ! $id ) { continue; } $selected_ids[] = $id; if ( ! empty( $item['featured'] ) ) { $featured_map[ $id ] = true; } } if ( empty( $selected_ids ) ) { return ''; } $terms = get_terms( array( 'taxonomy' => 'product_cat', 'include' => $selected_ids, 'hide_empty' => false, 'orderby' => 'include', ) ); } else { // All mode: exclude empty (optionally) and the actual "Uncategorized" category. $exclude_ids = array_map( 'absint', $exclude_categories ); // Only exclude the default product category if its slug is 'uncategorized'. $default_cat_id = absint( get_option( 'default_product_cat', 0 ) ); if ( $default_cat_id ) { $default_cat = get_term( $default_cat_id, 'product_cat' ); if ( $default_cat && ! is_wp_error( $default_cat ) && 'uncategorized' === $default_cat->slug ) { $exclude_ids[] = $default_cat_id; } } $exclude_ids = array_unique( array_filter( $exclude_ids ) ); $terms = get_terms( array( 'taxonomy' => 'product_cat', 'parent' => 0, 'hide_empty' => ! $show_empty, // phpcs:ignore WordPressVIPMinimum.Performance.WPQueryParams.PostNotIn_exclude -- get_terms() exclusion bounded to the default "uncategorized" product_cat term. 'exclude' => $exclude_ids, 'orderby' => 'menu_order', 'order' => 'ASC', ) ); $featured_map = array(); } // Bail silently if there are no categories to display. if ( is_wp_error( $terms ) || empty( $terms ) ) { return ''; } // --------------------------------------------------------------------------- // Build wrapper attributes. // --------------------------------------------------------------------------- $wrapper_attributes = get_block_wrapper_attributes( array( 'class' => 'airo-wp-product-categories-grid airo-wp-product-categories-grid--cols-' . $columns, 'role' => 'navigation', 'aria-label' => __( 'Product categories', 'airo-wp' ), 'style' => '--airo-wp-pcg-aspect-ratio: ' . esc_attr( $image_aspect_ratio ) . ';', ) ); // --------------------------------------------------------------------------- // Placeholder SVG (2×2 grid icon) for categories without an image. // --------------------------------------------------------------------------- $placeholder_svg = ''; ?>
> term_id ); $term_name = $term->name; // WooCommerce keeps a visibility-aware count (hidden and // out-of-stock-hidden products excluded, children included) in // term meta; the raw term count includes everything published. $woo_count = 'product_cat' === $term->taxonomy ? get_term_meta( $term_id, 'product_count_product_cat', true ) : ''; $term_count = is_numeric( $woo_count ) ? absint( $woo_count ) : absint( $term->count ); $term_url = get_term_link( $term ); if ( is_wp_error( $term_url ) ) { continue; } // Determine if this card should span two columns (manual mode only). $is_featured = isset( $featured_map[ $term_id ] ); // Resolve category image. $thumbnail_id = absint( get_term_meta( $term_id, 'thumbnail_id', true ) ); $image_html = ''; if ( $thumbnail_id ) { $image_html = wp_get_attachment_image( $thumbnail_id, 'medium_large', false, array( 'class' => 'airo-wp-product-categories-grid__image', 'alt' => '', 'loading' => 'lazy', ) ); } // Build card CSS classes. $card_class = 'airo-wp-product-categories-grid__card'; if ( $is_featured ) { $card_class .= ' airo-wp-product-categories-grid__card--featured'; } // Build accessible product count string. /* translators: %d: number of products in a category. */ $count_text = sprintf( _n( '%d product', '%d products', $term_count, 'airo-wp' ), $term_count ); ?>