phpwordpresswoocommercecustom-taxonomytaxonomy-terms

Limit product attributes displayed terms in WooCommerce product lists


I'm using code that displays attributes in the product list, archive and category page.

/* Show attributes in the product card */
add_action( 'woocommerce_shop_loop_item_title', 'new_template_loop_product_meta', 20 );
function new_template_loop_product_meta() {
    global $product;

    $attrs_by_cats = [
        21 => [ 'pa_size', 'pa_color' ],
        23 => [ 'pa_size', 'pa_color' ],
    ];

    $attr_list = [        
        'Size'  => 'pa_size',
        'Color' => 'pa_color',
    ];

    if ( ! is_object( $product ) ) {
        $product = wc_get_product( get_the_id() );
    }

    $cats = $product->get_category_ids();

    if ( ! is_array( $cats ) ) {
        return;
    }

    $attrs = [];

    foreach ( $cats as $cat ) {
        if ( isset( $attrs_by_cats[ $cat ] ) ) {
            $attrs[] = $attrs_by_cats[ $cat ];
        }
    }

    $allowed_attrs = array_unique( array_merge( [], ...$attrs ) );

    echo '<div class="pa">';

    foreach ( $attr_list as $attr_title => $attr_name ) {
        if ( in_array( $attr_name, $allowed_attrs, true ) ) {
            show_attribute( $product, $attr_title, $attr_name );
        }
    }

    echo '</div>';
}

/**
 * Show attribute.
 *
 * @param WC_Product $product    Product.
 * @param string     $attr_title Attribute title.
 * @param string     $attr_name  Attribute name.
 */
function show_attribute( $product, $attr_title, $attr_name ) {
    if ( 'sku' === $attr_name ) {
        $attr = (string) $product->get_sku();
    } else {
        $attr = $product->get_attribute( $attr_name );
    }

    if ( '' === $attr ) {
        return;
    }

    echo '<span class="pa-title">' . esc_html( $attr_title ) . ': </span><span class="pa-value">' . esc_html( $attr ) . '</span><br />';
}

There is a small problem here. If a product attribute has a lot of values (more than 10-20), then the layout of the card in the list of products breaks.

How to make it show the first 3-5 attributes and after that the number of other attributes? For example, Size: 30x40, 50x70, 70x70 +15.


Solution

  • First your current code can be largely simplified and optimized.

    To limit the number of displayed attributes terms, you need to:

    Try the following:

    /**
     * Display specific product attributes below shop loop item title.
     *
     * @return void
     */
    add_action( 'woocommerce_shop_loop_item_title', 'display_product_attribute_after_loop_item_title', 20 );
    function display_product_attribute_after_loop_item_title() {
        global $product;
    
        $targeted_cats  = [ 21, 23 ];
        $targeted_atts = [ 'pa_size', 'pa_color' ];
    
        if ( $product && has_term( $targeted_cats, 'product_cat', $product->get_id() ) ) {
            echo '<div class="pa">';
    
            foreach ( $targeted_atts as $taxonomy ) {
                display_product_attributes( $product, $taxonomy );
            }
            echo '</div>';
        }
    }
    
    /**
     * Utility function: Display specific product attribute terms.
     *
     * @param WC_Product $product    Product.
     * @param string     $taxonomy   Attribute taxonomy name.
     * @return void
     */
    function display_product_attributes( $product, $taxonomy ) {
        if ( 'sku' === $taxonomy ) {
            $attr_val = $product->get_sku();
        } 
        elseif ( $attr = $product->get_attribute( $taxonomy ) ) {
            $atts_start = 2; // Set the attributes start position
            $atts_limit = 3; // Set the limit of attributes terms 
    
            $atts_array = explode(', ', $attr ); // Convert attribute terms to an array 
            $atts_count = count($atts_array); // Get the number of attributes terms
    
            // Limit the number of attributes to be displayed
            if ( $atts_count > $atts_limit ) {
                // Handling when count is below (start + limit)
                if ( $atts_count < ( $atts_limit + $atts_start ) ) {
                    $atts_start = $atts_count - $atts_limit; // Adjust start index slice
                } 
                $atts_array = array_slice($atts_array, $atts_start, $atts_limit, true); // Limit attributes (slice)
                $attr = implode(', ', $atts_array ) . ' <span class="plus-count">+' . ( $atts_count - $atts_limit ) . '</span>'; // Convert attributes array to string
            }
        }
    
        if ( isset($attr) && ! empty($attr) ) {
            printf( '<div class="%s"><span class="pa-title">%s: </span><span class="pa-value">%s</span></div>', $taxonomy, wc_attribute_label( $taxonomy, $product ), $attr );
        }
    }
    

    It should work as you expect.