skip to Main Content

I’m trying to create an archive page for "on Sale" products on my WooCommerce site, I mean an archive page, with widgets, category and attribute filters, I know I can just display "on Sale" products with a shortcode or a Gutenberg Block.

I can’t seem to find a way to do that.

After some search, I found OnSale Page for WooCommerce plugin. It works to create the archive page, but then all the filters by attribute or category keep redirecting me to the shop page, I tried to contact support, but the developers don’t seem to work on it anymore.

Has anyone faced something like this?

2

Answers


  1. Chosen as BEST ANSWER

    Expanding the answer from @loictheaztec, the best solution is to have an extra option in the sorting options

    I've added a bit of extra code to make it work with the "Product attribute filter" and "Product Categories List" widgets, both will display only options with sale products available and avoid getting empty lists

    /**
     * Add orderby options for onsale
     */
    function enable_catalog_on_sale( $args ) {
    
        if ( isset( $_GET['orderby'] ) ) {
            if ( 'on-sale' == $_GET['orderby'] ) {
                return array( 'orderby'  => 'menu_order title', 'order' => 'ASC' );
            }
        }
        return $args;
    }
    add_filter( 'woocommerce_get_catalog_ordering_args', 'enable_catalog_on_sale' );
    
    /**
     * Add new product orderby option
     */
    function add_catalog_on_sale( $orderby_options ) {
        $orderby_options['on-sale']  = __("Solo en oferta", "woocommerce");
        return $orderby_options;
    }
    add_filter( 'woocommerce_catalog_orderby', 'add_catalog_on_sale' );
    
    /**
     * Filter WooCommerce query to only include on sale products when 'orderby === 'on-sale'
     */
    function product_query_on_sale( $q ) {
        if ( ! is_admin() && isset( $_GET['orderby'] ) && $_GET['orderby'] === 'on-sale' ) {
            $q->set( 'post__in', array_merge( array( 0 ), wc_get_product_ids_on_sale() ) );
        }
    }
    add_action( 'woocommerce_product_query', 'product_query_on_sale' );
    
    /**
     * Hook the WC Term Product Count Query to filter only for sale products
     */
    function filter_term_product_query_sale( $query ) {
        if ( ! is_admin() && isset( $_GET['orderby'] ) && $_GET['orderby'] === 'on-sale' ) {
            $query['where'] .= ' AND wp_posts.ID IN (' . implode(',', wc_get_product_ids_on_sale()) . ')';
        }
        return $query;
    }
    add_filter('woocommerce_get_filtered_term_product_counts_query', 'filter_term_product_query_sale' );
    
    /**
     * Get product categories IDs with products on sale
     */
    function get_product_cat_ids_with_sale_products() {
        global $wpdb;
    
        $query = "
                SELECT DISTINCT t.term_id
                FROM {$wpdb->terms} AS t
                JOIN {$wpdb->term_taxonomy} AS tt ON t.term_id = tt.term_id
                JOIN {$wpdb->term_relationships} AS tr ON tt.term_taxonomy_id = tr.term_taxonomy_id
                JOIN {$wpdb->posts} AS p ON tr.object_id = p.ID
                JOIN {$wpdb->postmeta} AS pm ON p.ID = pm.post_id
                WHERE tt.taxonomy = 'product_cat'
                AND pm.meta_key = '_sale_price'
                AND pm.meta_value != ''
        ";
        $category_ids = $wpdb->get_col($query);
        return $category_ids;
    }
    
    /**
     * Hook on arguments for wp_list_categories for WooCommerce Categories Widget to get only the ones with sale products
     */
    function add_orderby_product_category_link( $args ) {
        if ( ! is_admin() && isset( $_GET['orderby'] ) && $_GET['orderby'] === 'on-sale' ) {
            $args['include'] = get_product_cat_ids_with_sale_products();
        }
        return $args;
    }
    add_filter('woocommerce_product_categories_widget_args', 'add_orderby_product_category_link', 10, 3 );
    

    Also, to make the categories links in widget gets the orderby parameter, I've used som Javascript

    document.addEventListener("DOMContentLoaded", function() {
      // Get the query string from the URL
      var queryString = window.location.search;
    
      // Function to get the value of a parameter from the query string
      function getParameterValue(parameter) {
        var parameterIndex = queryString.indexOf(parameter);
        if (parameterIndex !== -1) {
          var start = parameterIndex + parameter.length + 1; // +1 to skip the '=' sign
          var end = queryString.indexOf('&', start) !== -1 ? queryString.indexOf('&', start) : queryString.length;
          return queryString.substring(start, end);
        }
        return null;
      }
    
      // Find all links within elements with class "widget_product_categories"
      var widgetLinks = document.querySelectorAll('.widget_product_categories a');
    
      // Loop through the links and add the "orderby" parameter
      widgetLinks.forEach(function(link) {
        var href = link.getAttribute('href');
        var existingOrderbyValue = getParameterValue('orderby');
        if (existingOrderbyValue && href.indexOf('orderby') === -1) {
          var delimiter = href.includes('?') ? '&' : '?';
          link.setAttribute('href', href + delimiter + 'orderby=' + existingOrderbyValue);
        }
      });
    });
    

  2. What you can do instead is to filter products from sorting options:

    enter image description here

    The following code will enable that, allowing to use with it all other filters, from widgets:

    add_filter( 'woocommerce_get_catalog_ordering_args', 'enable_catalog_on_sale' );
    function enable_catalog_on_sale( $args ) {
    
        if ( isset( $_GET['orderby'] ) ) {
            if ( 'on-sale' == $_GET['orderby'] ) {
                return array( 'orderby'  => 'menu_order title', 'order' => 'ASC' );
            }
        }
        return $args;
    }
    
    add_filter( 'woocommerce_catalog_orderby', 'add_catalog_on_sale' );
    function add_catalog_on_sale( $orderby_options ) {
        $orderby_options['on-sale']  = __("Show on sale products", "woocommerce");
        return $orderby_options;
    }
    
    add_action( 'woocommerce_product_query', 'product_query_on_sale' );
    function product_query_on_sale( $q ) {
        if ( ! is_admin() && isset( $_GET['orderby'] ) && $_GET['orderby'] === 'on-sale' ) {
            $q->set( 'post__in', array_merge( array( 0 ), wc_get_product_ids_on_sale() ) );
        }
    }
    

    Code goes in functions.php file of your child theme (or in a plugin). Tested and works.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search