Woocommerce, sort dropdown on shortcode based product lists
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 :).
Louisa
Updated on June 04, 2022Comments
-
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:
- No sorting dropdown appears above the product lists, so the products cannot be sorted by our visitors.
- 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?