• Resolved panda

    (@alejorostata)


    I used the widget Rating Filter, on my shop page it will only show if there are available products to filter by rating. I want to show the Rating Filter (1 to 5) even no available products nor no ratings yet. Is this possible, is there a hook for that?

Viewing 6 replies - 1 through 6 (of 6 total)
  • Plugin Support RK a11n

    (@riaanknoetze)

    Hi there,

    Can you share a bit more on the use-case here? The reason I’m asking is that if there are no products (with ratings) to filter, what should the widget then filter?

    Thread Starter panda

    (@alejorostata)

    Hello! It doesn’t matter if there are no products to filter, I just want to keep it viewable for the sake of the UI.

    Plugin Support Job a11n

    (@jobthomas)

    Automattic Happiness Engineer

    In order to do so, you’d need to alter the widget to no longer count if there are products in that rating. This is the section you’re looking for:

    https://github.com/woocommerce/woocommerce/blob/0d901a2ecadc7ea52aa4a875d511d5097d50cbed/includes/widgets/class-wc-widget-rating-filter.php#L108-L135

    Thread Starter panda

    (@alejorostata)

    Can you help me a little? I lost in the width of the star. I managed to show them all but the size of the star 5 to 1 is a little messy because of the calculation of rating.

    for ( $rating = 5; $rating >= 1; $rating-- ) {
    			$count = $this->get_filtered_product_count( $rating );
    			/*if ( empty( $count ) ) {
    				continue;
    			}*/
    			$found = true;
    			$link  = $base_link;
    
    			if ( in_array( $rating, $rating_filter, true ) ) {
    				$link_ratings = implode( ',', array_diff( $rating_filter, array( $rating ) ) );
    			} else {
    				$link_ratings = implode( ',', array_merge( $rating_filter, array( $rating ) ) );
    			}
    
    			$class       = in_array( $rating, $rating_filter, true ) ? 'wc-layered-nav-rating chosen' : 'wc-layered-nav-rating';
    			$link        = apply_filters( 'woocommerce_rating_filter_link', $link_ratings ? add_query_arg( 'rating_filter', $link_ratings, $link ) : remove_query_arg( 'rating_filter' ) );
    
                         /** This is responsible for the width of the golden color of rating. **/			
                         $rating_html = wc_get_star_rating_html( $rating );
    			
                     
    			$count_html  = wp_kses(
    				apply_filters( 'woocommerce_rating_filter_count', "({$count})", $count, $rating ),
    				array(
    					'em'     => array(),
    					'span'   => array(),
    					'strong' => array(),
    				)
    			);
    
    			printf( '<li class="%s"><a href="%s"><span class="star-rating">%s</span> %s</a></li>', esc_attr( $class ), esc_url( $link ), $rating_html, $count_html ); // WPCS: XSS ok.
    		}
    • This reply was modified 4 years, 3 months ago by panda.
    Thread Starter panda

    (@alejorostata)

    Alright, I managed to do it.

    I hope there is an easy way to do this, probably a hook, action, or filter to disable the counter and display them all. Anyway, by extending the class of WC_Widget_Rating_Filter I managed to override the widget.

    Here’s the full source in my functions.php.

    /**
     * Widget rating filter class.
     */
    class Custom_Widget_Rating_Filter extends WC_Widget_Rating_Filter {
    
    	/**
    	 * Constructor.
    	 */
    	public function __construct() {
    		$this->widget_cssclass    = 'woocommerce widget_rating_filter';
    		$this->widget_description = __( 'Display a list of star ratings to filter products in your store.', 'woocommerce' );
    		$this->widget_id          = 'woocommerce_rating_filter';
    		$this->widget_name        = __( 'Filter Products by Rating', 'woocommerce' );
    		$this->settings           = array(
    			'title' => array(
    				'type'  => 'text',
    				'std'   => __( 'Average rating', 'woocommerce' ),
    				'label' => __( 'Title', 'woocommerce' ),
    			),
    		);
    		parent::__construct();
    	}
    
    	/**
    	 * Count products after other filters have occurred by adjusting the main query.
    	 *
    	 * @param  int $rating Rating.
    	 * @return int
    	 */
    	protected function get_filtered_product_count( $rating ) {
    		global $wpdb;
    
    		$tax_query  = WC_Query::get_main_tax_query();
    		$meta_query = WC_Query::get_main_meta_query();
    
    		// Unset current rating filter.
    		foreach ( $tax_query as $key => $query ) {
    			if ( ! empty( $query['rating_filter'] ) ) {
    				unset( $tax_query[ $key ] );
    				break;
    			}
    		}
    
    		// Set new rating filter.
    		$product_visibility_terms = wc_get_product_visibility_term_ids();
    		$tax_query[]              = array(
    			'taxonomy'      => 'product_visibility',
    			'field'         => 'term_taxonomy_id',
    			'terms'         => $product_visibility_terms[ 'rated-' . $rating ],
    			'operator'      => 'IN',
    			'rating_filter' => true,
    		);
    
    		$meta_query     = new WP_Meta_Query( $meta_query );
    		$tax_query      = new WP_Tax_Query( $tax_query );
    		$meta_query_sql = $meta_query->get_sql( 'post', $wpdb->posts, 'ID' );
    		$tax_query_sql  = $tax_query->get_sql( $wpdb->posts, 'ID' );
    
    		$sql  = "SELECT COUNT( DISTINCT {$wpdb->posts}.ID ) FROM {$wpdb->posts} ";
    		$sql .= $tax_query_sql['join'] . $meta_query_sql['join'];
    		$sql .= " WHERE {$wpdb->posts}.post_type = 'product' AND {$wpdb->posts}.post_status = 'publish' ";
    		$sql .= $tax_query_sql['where'] . $meta_query_sql['where'];
    
    		$search = WC_Query::get_main_search_query_sql();
    		if ( $search ) {
    			$sql .= ' AND ' . $search;
    		}
    
    		return absint( $wpdb->get_var( $sql ) ); // WPCS: unprepared SQL ok.
    	}
    
    	/**
    	 * Widget function.
    	 *
    	 * @see WP_Widget
    	 * @param array $args     Arguments.
    	 * @param array $instance Widget instance.
    	 */
    	public function widget( $args, $instance ) {
    		if ( ! is_shop() && ! is_product_taxonomy() ) {
    			return;
    		}
    
    		if ( ! WC()->query->get_main_query()->post_count ) {
    			return;
    		}
    
    		ob_start();
    
    		$found         = false;
    		$rating_filter = isset( $_GET['rating_filter'] ) ? array_filter( array_map( 'absint', explode( ',', wp_unslash( $_GET['rating_filter'] ) ) ) ) : array(); // WPCS: input var ok, CSRF ok, sanitization ok.
    		$base_link     = remove_query_arg( 'paged', $this->get_current_page_url() );
    
    		$this->widget_start( $args, $instance );
    
    		echo '<ul>';
    
    		for ( $rating = 5; $rating >= 1; $rating-- ) {
    			$count = $this->get_filtered_product_count( $rating );
    			/*
    			Responsible for showing available rating filter only.
    			if ( empty( $count ) ) {
    				continue;
    			}*/
    			$found = true;
    			$link  = $base_link;
    
    			if ( in_array( $rating, $rating_filter, true ) ) {
    				$link_ratings = implode( ',', array_diff( $rating_filter, array( $rating ) ) );
    			} else {
    				$link_ratings = implode( ',', array_merge( $rating_filter, array( $rating ) ) );
    			}
    
    			$class       = in_array( $rating, $rating_filter, true ) ? 'wc-layered-nav-rating chosen' : 'wc-layered-nav-rating';
    			$link        = apply_filters( 'woocommerce_rating_filter_link', $link_ratings ? add_query_arg( 'rating_filter', $link_ratings, $link ) : remove_query_arg( 'rating_filter' ) );
    			/*  Responsible for showing rating span (gold|width)
    				$rating_html = wc_get_star_rating_html( $rating );
    			*/
    			$count_html  = wp_kses(
    				apply_filters( 'woocommerce_rating_filter_count', "({$count})", $count, $rating ),
    				array(
    					'em'     => array(),
    					'span'   => array(),
    					'strong' => array(),
    				)
    			);
    			
    			$rating_html = '<span style="width:'.(($rating/5)*100).'%">Rated <strong class="rating">'.$rating.'</strong> out of '.$rating.'</span>';
    		
    			printf( '<li class="%s"><a href="%s"><span class="star-rating">%s</span> %s</a></li>', esc_attr( $class ), esc_url( $link ), $rating_html, $count_html ); // WPCS: XSS ok.
    		}
    
    		echo '</ul>';
    
    		$this->widget_end( $args );
    
    		if ( ! $found ) {
    			ob_end_clean();
    		} else {
    			echo ob_get_clean(); // WPCS: XSS ok.
    		}
    	}
    }
    
    //handling the widget registration
    function my_widget_price_filter_register() {
    	unregister_widget( 'WC_Widget_Rating_Filter' ); //unregister the default
    	register_widget( 'Custom_Widget_Rating_Filter' ); //register the new one above.
    } 
    add_action( 'widgets_init', 'my_widget_price_filter_register' );

    then in the CSS I override the width because the default one is 6.5em which will make the fill color overlap.

    .widget_rating_filter li.wc-layered-nav-rating span.star-rating{
    	width: 6em !important; 
    }

    I hope this helps someone who wants to make the UI of their store more beautiful.

    • This reply was modified 4 years, 3 months ago by panda.
    Thread Starter panda

    (@alejorostata)

    UPDATED: I forgot to remove the condition;

    if ( ! WC()->query->get_main_query()->post_count ) {
       return;
    }

    In functions.php, the code should be:

    class Custom_Widget_Rating_Filter extends WC_Widget_Rating_Filter {
    
    	/**
    	 * Constructor.
    	 */
    	public function __construct() {
    		$this->widget_cssclass    = 'woocommerce widget_rating_filter';
    		$this->widget_description = __( 'Display a list of star ratings to filter products in your store.', 'woocommerce' );
    		$this->widget_id          = 'woocommerce_rating_filter';
    		$this->widget_name        = __( 'Filter Products by Rating', 'woocommerce' );
    		$this->settings           = array(
    			'title' => array(
    				'type'  => 'text',
    				'std'   => __( 'Average rating', 'woocommerce' ),
    				'label' => __( 'Title', 'woocommerce' ),
    			),
    		);
    		parent::__construct();
    	}
    
    	/**
    	 * Count products after other filters have occurred by adjusting the main query.
    	 *
    	 * @param  int $rating Rating.
    	 * @return int
    	 */
    	protected function get_filtered_product_count( $rating ) {
    		global $wpdb;
    
    		$tax_query  = WC_Query::get_main_tax_query();
    		$meta_query = WC_Query::get_main_meta_query();
    
    		// Unset current rating filter.
    		foreach ( $tax_query as $key => $query ) {
    			if ( ! empty( $query['rating_filter'] ) ) {
    				unset( $tax_query[ $key ] );
    				break;
    			}
    		}
    
    		// Set new rating filter.
    		$product_visibility_terms = wc_get_product_visibility_term_ids();
    		$tax_query[]              = array(
    			'taxonomy'      => 'product_visibility',
    			'field'         => 'term_taxonomy_id',
    			'terms'         => $product_visibility_terms[ 'rated-' . $rating ],
    			'operator'      => 'IN',
    			'rating_filter' => true,
    		);
    
    		$meta_query     = new WP_Meta_Query( $meta_query );
    		$tax_query      = new WP_Tax_Query( $tax_query );
    		$meta_query_sql = $meta_query->get_sql( 'post', $wpdb->posts, 'ID' );
    		$tax_query_sql  = $tax_query->get_sql( $wpdb->posts, 'ID' );
    
    		$sql  = "SELECT COUNT( DISTINCT {$wpdb->posts}.ID ) FROM {$wpdb->posts} ";
    		$sql .= $tax_query_sql['join'] . $meta_query_sql['join'];
    		$sql .= " WHERE {$wpdb->posts}.post_type = 'product' AND {$wpdb->posts}.post_status = 'publish' ";
    		$sql .= $tax_query_sql['where'] . $meta_query_sql['where'];
    
    		$search = WC_Query::get_main_search_query_sql();
    		if ( $search ) {
    			$sql .= ' AND ' . $search;
    		}
    
    		return absint( $wpdb->get_var( $sql ) ); // WPCS: unprepared SQL ok.
    	}
    
    	/**
    	 * Widget function.
    	 *
    	 * @see WP_Widget
    	 * @param array $args     Arguments.
    	 * @param array $instance Widget instance.
    	 */
    	public function widget( $args, $instance ) {
    		if ( ! is_shop() && ! is_product_taxonomy() ) {
    			return;
    		}
    
    		/* Responsible for not showing the STAR when query returns empty
    		if ( ! WC()->query->get_main_query()->post_count ) {
    			return;
    		}*/
    
    		ob_start();
    
    		$found         = false;
    		$rating_filter = isset( $_GET['rating_filter'] ) ? array_filter( array_map( 'absint', explode( ',', wp_unslash( $_GET['rating_filter'] ) ) ) ) : array(); // WPCS: input var ok, CSRF ok, sanitization ok.
    		$base_link     = remove_query_arg( 'paged', $this->get_current_page_url() );
    
    		$this->widget_start( $args, $instance );
    
    		echo '<ul>';
    
    		for ( $rating = 5; $rating >= 1; $rating-- ) {
    			$count = $this->get_filtered_product_count( $rating );
    			
    			/*Responsible for not showing all the 5 star rating
    			if ( empty( $count ) ) {
    				continue;
    			}*/
    			$found = true;
    			$link  = $base_link;
    
    			if ( in_array( $rating, $rating_filter, true ) ) {
    				$link_ratings = implode( ',', array_diff( $rating_filter, array( $rating ) ) );
    			} else {
    				$link_ratings = implode( ',', array_merge( $rating_filter, array( $rating ) ) );
    			}
    
    			$class       = in_array( $rating, $rating_filter, true ) ? 'wc-layered-nav-rating chosen' : 'wc-layered-nav-rating';
    			$link        = apply_filters( 'woocommerce_rating_filter_link', $link_ratings ? add_query_arg( 'rating_filter', $link_ratings, $link ) : remove_query_arg( 'rating_filter' ) );
    			/*  Responsible for showing rating span (gold|width)
    				$rating_html = wc_get_star_rating_html( $rating );
    			*/
    			$count_html  = wp_kses(
    				apply_filters( 'woocommerce_rating_filter_count', "({$count})", $count, $rating ),
    				array(
    					'em'     => array(),
    					'span'   => array(),
    					'strong' => array(),
    				)
    			);
    			
    			$rating_html = '<span style="width:'.(($rating/5)*100).'%">Rated <strong class="rating">'.$rating.'</strong> out of '.$rating.'</span>';
    			
    			$checked = '';
    			if(in_array( $rating, $rating_filter, true )){
    				$checked = '<i class="fas fa-check-circle csx-attribute-active"></i><i class="fas fa-times-circle csx-attribute-active-remove"></i>';
    			}else{
    				$checked = '<i class="fas fa-check-circle csx-attribute-not-active"></i>';
    			}
    		
    			printf( '<li class="%s"><a href="%s"><span class="star-rating">%s</span> %s</a>%s</li>', esc_attr( $class ), esc_url( $link ), $rating_html, $count_html, $checked); // WPCS: XSS ok.
    		}
    
    		echo '</ul>';
    
    		$this->widget_end( $args );
    
    		if ( ! $found ) {
    			ob_end_clean();
    		} else {
    			echo ob_get_clean(); // WPCS: XSS ok.
    		}
    	}
    }
    
    //handling the widget registration
    function my_widget_price_filter_register() {
    	unregister_widget( 'WC_Widget_Rating_Filter' ); //unregister the default
    	register_widget( 'Custom_Widget_Rating_Filter' ); //register the new one above.
    } 
    add_action( 'widgets_init', 'my_widget_price_filter_register' );

    No need to change the CSS but if you do it should be;

    .widget_rating_filter li.wc-layered-nav-rating span.star-rating{
    	width: 6em !important; /* change this */
    }
    • This reply was modified 4 years, 3 months ago by panda.
Viewing 6 replies - 1 through 6 (of 6 total)
  • The topic ‘Making Rating Filter always show’ is closed to new replies.