Woocommerce, sort dropdown on shortcode based product lists

10,408

Solution 1

Regarding your first issue, I have not found any good solutions beside hacking the shortcode within WC. Not entirely advisable as it will be overwritten each upgrade/patch of WC. If it's absolutely necessary, you could maintain the code by rewriting the hack each time you upgrade.

Alright, first download a copy of includes\class-wc-shortcodes.php in your woocommerce folder. Make a backup of the original before editing it, I usually rename the file with a -o or change the file type to .bak.

Assuming you'd want the original sort by dropdown that comes with WC on the Shop Page.

Step 1 Remove orderby and order arguments on the shortcode:

[product_category category="boots" per_page="20" columns="4"]

Step 2 Edit the Product Category shortcode on class-wc-shortcodes.php as such:

/**
 * List products in a category shortcode
 *
 * @access public
 * @param array $atts
 * @return string
 */
public static function product_category( $atts ) {
    global $woocommerce_loop, $wpdb;

    if ( empty( $atts ) ) return '';

    extract( shortcode_atts( array(
        'per_page'      => '12',
        'columns'       => '4',
        'orderby'       => 'title',
        'order'         => 'asc',
        'category'      => '',
        'operator'      => 'IN' // Possible values are 'IN', 'NOT IN', 'AND'.
        ), $atts ) );

    if ( ! $category ) return '';

    // Default ordering args
    // $ordering_args = WC()->query->get_catalog_ordering_args( $orderby, $order ); // COMMENT THIS OUT
    $orderby = 'title';
    $order = 'asc';
    if ( isset( $_GET['orderby'] ) ) {
        $getorderby = $_GET['orderby'];
    }
    if ($getorderby == 'popularity') {
        $orderby = 'meta_value_num';
        $order = 'desc';
        $meta_key = 'total_sales';
    } elseif ($getorderby == 'rating') {

        $fields .= ", AVG( $wpdb->commentmeta.meta_value ) as average_rating ";

        $where .= " AND ( $wpdb->commentmeta.meta_key = 'rating' OR $wpdb->commentmeta.meta_key IS null ) ";

        $join .= "
            LEFT OUTER JOIN $wpdb->comments ON($wpdb->posts.ID = $wpdb->comments.comment_post_ID)
            LEFT JOIN $wpdb->commentmeta ON($wpdb->comments.comment_ID = $wpdb->commentmeta.comment_id)
        ";

        $orderby = "average_rating DESC, $wpdb->posts.post_date DESC";

        $groupby = "$wpdb->posts.ID";   

    } elseif ($getorderby == 'date') {
        $orderby = 'date';
        $order = 'desc';
    } elseif ($getorderby == 'price') {
        $orderby = 'meta_value_num';
        $order = 'asc';
        $meta_key = '_price';
    } elseif ($getorderby == 'price-desc') {
        $orderby = 'meta_value_num';
        $order = 'desc';
        $meta_key = '_price';
    }
    $args = array(
        'post_type'             => 'product',
        'post_status'           => 'publish',
        'ignore_sticky_posts'   => 1,
        'orderby'               => $orderby, // $ordering_args['orderby'],
        'order'                 => $order, // $ordering_args['order'],
        'meta_key'              => $meta_key,
        'fields'                => $fields,
        'where'                 => $where,
        'join'                  => $join,
        'groupby'               => $groupby,
        'posts_per_page'        => $per_page,
        'meta_query'            => array(
            array(
                'key'           => '_visibility',
                'value'         => array('catalog', 'visible'),
                'compare'       => 'IN'
            )
        ),
        'tax_query'             => array(
            array(
                'taxonomy'      => 'product_cat',
                'terms'         => array( esc_attr( $category ) ),
                'field'         => 'slug',
                'operator'      => $operator
            )
        )
    );

    if ( isset( $ordering_args['meta_key'] ) ) {
        $args['meta_key'] = $ordering_args['meta_key'];
    }

    ob_start();

    $products = new WP_Query( apply_filters( 'woocommerce_shortcode_products_query', $args, $atts ) );

    $woocommerce_loop['columns'] = $columns;

    if ( $products->have_posts() ) : ?>

    <div style="width:100%;">
        <div style="float:right">
            <form class="woocommerce-ordering" method="get">
                <select name="orderby" class="orderby">
                    <?php
                        $catalog_orderby = apply_filters( 'woocommerce_catalog_orderby', array(
                            'menu_order' => __( 'Default sorting', 'woocommerce' ),
                            'popularity' => __( 'Sort by popularity', 'woocommerce' ),
                            'rating'     => __( 'Sort by average rating', 'woocommerce' ),
                            'date'       => __( 'Sort by newness', 'woocommerce' ),
                            'price'      => __( 'Sort by price: low to high', 'woocommerce' ),
                            'price-desc' => __( 'Sort by price: high to low', 'woocommerce' )
                        ) );

                        if ( get_option( 'woocommerce_enable_review_rating' ) === 'no' )
                            unset( $catalog_orderby['rating'] );

                        foreach ( $catalog_orderby as $id => $name )
                            echo '<option value="' . esc_attr( $id ) . '" ' . selected( $getorderby, $id, false ) . '>' . esc_attr( $name ) . '</option>';
                    ?>
                </select>
                <?php
                    // Keep query string vars intact
                    foreach ( $_GET as $key => $val ) {
                        if ( 'orderby' === $key || 'submit' === $key )
                            continue;

                        if ( is_array( $val ) ) {
                            foreach( $val as $innerVal ) {
                                echo '<input type="hidden" name="' . esc_attr( $key ) . '[]" value="' . esc_attr( $innerVal ) . '" />';
                            }

                        } else {
                            echo '<input type="hidden" name="' . esc_attr( $key ) . '" value="' . esc_attr( $val ) . '" />';
                        }
                    }
                ?>
            </form>
        </div>
    </div>
    <div style="clear:both;"></div>

        <?php woocommerce_product_loop_start(); ?>

            <?php while ( $products->have_posts() ) : $products->the_post(); ?>

                <?php wc_get_template_part( 'content', 'product' ); ?>

            <?php endwhile; // end of the loop. ?>

        <?php woocommerce_product_loop_end(); ?>

    <?php endif;

    woocommerce_reset_loop();
    wp_reset_postdata();

    return '<div class="woocommerce columns-' . $columns . '">' . ob_get_clean() . '</div>';
}

Upload the file back into the includes folder and you're done! Your shortcode product pages will now have a working sort by dropdown same as the one that appears on the WC Shop Page.

Edit the sort options to your liking! Hope it helps!

Solution 2

Well for your second problem: Your shortcode is limiting to see only 20 products. Change the it to per_page="40" and you should see 40 products or simply remove the line and the number of products is not limited.

For your first problem I don't have an answer. I'm looking it for my self as well :).

Share:
10,408
Louisa
Author by

Louisa

Updated on June 04, 2022

Comments

  • Louisa
    Louisa almost 2 years

    In our shop, we have a number of standard WP pages. On these pages we show ~40 products using the standard Woocommerce shortcodes.

    For example:

    [product_category category="boots" per_page="20" columns="4" orderby="price" order="desc"]
    

    The products appears, but there are two things missing:

    1. No sorting dropdown appears above the product lists, so the products cannot be sorted by our visitors.
    2. We don't see any pagination buttons, so it's impossible to see more than the first 20 products on each page.

    Any ideas how we can fix those two things?