Custom Order By Attributes

This tutorial will teach you as to how you can add custom sorting or ordering of product by its attribute in shop page.

WooCommerce comes up with a few useful product order, which is quite handy as small website, but if you want to increase your user experience and want to reduce bounce rate then you might have to give user few more option for filtering, ordering, etc., so in this tutorial we 'll discussing as how easily you can add such options we few lines of code. So tighten your seat-belt. 😉

Directly we cannot short product with attributes/taxonomy in WooCommerce shop page, so what we have to do is first save the attributes in postmeta from there we can sort product by using meta_value. To do this we have to use save_post hook which is called on every post/product create or update.

<?php
/**
 * Save product attributes to post metadata when a product is saved.
 *
 * @param int $post_id The post ID.
 * @param post $post The post object.
 * @param bool $update Whether this is an existing post being updated or not.
 *  
 * Refrence: https://codex.wordpress.org/Plugin_API/Action_Reference/save_post
 */
function wh_save_product_custom_meta($post_id, $post, $update) {
    $post_type = get_post_type($post_id);
    // If this isn't a 'product' post, don't update it.
    if ($post_type != 'product')
        return;

    if (!empty($_POST['attribute_names']) && !empty($_POST['attribute_values'])) {
        $attribute_names = $_POST['attribute_names'];
        $attribute_values = $_POST['attribute_values'];
        foreach ($attribute_names as $key => $attribute_name) {
            switch ($attribute_name) {
                //for color (string)
                case 'pa_color':
                    //it may have multiple color (eg. black, brown, maroon, white) but we'll take only the first color.
                    if (!empty($attribute_values[$key][0])) {
                        update_post_meta($post_id, 'pa_color', $attribute_values[$key][0]);
                    }
                    break;
                //for lenght (int)
                case 'pa_length':
                    if (!empty($attribute_values[$key][0])) {
                        update_post_meta($post_id, 'pa_length', $attribute_values[$key][0]);
                    }
                    break;
                default:
                    break;
            }
        }
    }
}

add_action( 'save_post', 'wh_save_product_custom_meta', 10, 3);

Now that we have added attributes to postmeta now we have to build the logic for ordering them

/**
* Main ordering logic for orderby attribute
* Refrence: https://docs.woocommerce.com/document/custom-sorting-options-ascdesc/
*/
add_filter('woocommerce_get_catalog_ordering_args', 'wh_catalog_ordering_args');

function wh_catalog_ordering_args($args) {
global $wp_query;
if (isset($_GET['orderby'])) {
switch ($_GET['orderby']) {
//for attribute/taxonomy=pa_color
case 'pa-color-asc' :
$args['order'] = 'ASC';
$args['meta_key'] = 'pa_color';
$args['orderby'] = 'meta_value';
break;
case 'pa-color-desc' :
$args['order'] = 'DESC';
$args['meta_key'] = 'pa_color';
$args['orderby'] = 'meta_value';
break;
//for attribute/taxonomy=pa_length
case 'pa-length-asc' :
$args['order'] = 'ASC';
$args['meta_key'] = 'pa_length';
$args['orderby'] = 'meta_value_num';
break;
case 'pa-length-desc' :
$args['order'] = 'DESC';
$args['meta_key'] = 'pa_length';
$args['orderby'] = 'meta_value_num';
break;
}
}
return $args;
}

Now we are all set let's give Site Admin and User the control to chose shorting option.

/**
* Lets add the created sorting order to the dropdown list.
* Refrence: http://hookr.io/filters/woocommerce_catalog_orderby/
*/
//To under Default Product Sorting in Dashboard > WooCommerce > Settings > Products > Display.
add_filter( 'woocommerce_default_catalog_orderby_options', 'wh_catalog_orderby' );
add_filter('woocommerce_catalog_orderby', 'wh_catalog_orderby');

function wh_catalog_orderby($sortby) {
$sortby['pa-color-asc'] = 'Sort by Color: A - Z';
$sortby['pa-color-desc'] = 'Sort by Color: Z - A';
$sortby['pa-length-asc'] = 'Sort by Length: Small - Large';
$sortby['pa-length-desc'] = 'Sort by Length: Large - Small';
return $sortby;
}

Let me know in the comments if you face any problem while implementing it or you have a better solution to it, then surely we will love to hear form you.

Raunak Gupta

Raunak Gupta

I'm Raunak Gupta, a seasoned software developer with over 9 years of experience in a wide range of programming languages, frameworks, and tools. I started my journey as a WordPress & CakePHP developer in 2014, diving deep into the world of OOPs, Request handling, and SEO. Along the way, I crafted numerous dazzling WooCommerce stores, tamed payment gateways, optimized for full filament functionality, and achieved ultra-low latency for lightning-fast load times. My expertise extends to BI tools, website builders, DevOps, and team leadership. I like to help upcoming developers, so I share my experience through this blog and by assisting fellow developers on Stack Overflow, where I've earned a stellar reputation with over 10k+ points of recognition.

Articles: 29

20 Comments

  1. I’m not fond of adding multiple keys in meta table and ordering the product by that. Isn’t there any better way?

    • Hi Adalie,
      Till now I haven’t found out any better way, but if you got one then do share with us, I would love to know.

  2. Hi Raunak, in my case not execute. Not show the attributes, I don’t know what happens.
    My WordPress version is 4.7.3

    Can you help me?

  3. Hi I have searched for a code like this for several days. In my case, the attributes are for beers and I need to filter by attribute value, alcohol level, bitterness and color. try to adapt your code, it does not give errors but I can not make it work. I apologize for my bad English but use a translator since I speak Spanish. Regards

    • Hi Miguel,

      I missed out to add save_post action. Now I have updated my post, so it should be working now.
      And just for you, I have translated the above line in Spanish 😉

      “Me perdí para agregar la acción save_post. Ahora actualicé mi publicación, por lo que debería estar funcionando ahora.”

  4. tried this code but sorting is not being done, have created an attribute named colour and changed in all the codes given above from color to colour. But sorting of asc to desc or vice-versa not happening, it displays a blank page.

    • Hi Malar,

      Can you please share you code that you have written, and have you added add_action( 'save_post', 'wh_save_product_custom_meta', 10, 3); ?

  5. The code is not working what i tried .
    function wh_save_product_custom_meta($post_id, $post, $update) {
    $post_type = get_post_type($post_id);
    // If this isn’t a ‘product’ post, don’t update it.
    if ($post_type != ‘product’)
    return;

    if (!empty($_POST[‘attribute_names’]) && !empty($_POST[‘attribute_values’])) {
    $attribute_names = $_POST[‘attribute_names’];
    $attribute_values = $_POST[‘attribute_values’];
    foreach ($attribute_names as $key => $attribute_name) {
    switch ($attribute_name) {
    //for lenght (int)
    case ‘pa_stamp-year’:
    if (!empty($attribute_values[$key][0])) {
    update_post_meta($post_id, ‘pa_stamp-year’, $attribute_values[$key][0]);
    }
    break;
    default:
    break;
    }
    }
    }
    }

    add_action( ‘save_post’, ‘wh_save_product_custom_meta’, 10, 3);

    add_filter(‘woocommerce_get_catalog_ordering_args’, ‘wh_catalog_ordering_args’);

    function wh_catalog_ordering_args($args) {
    global $wp_query;
    if (isset($_GET[‘orderby’])) {
    switch ($_GET[‘orderby’]) {

    case ‘pa_stamp-year’ :
    $args[‘order’] = ‘ASC’;
    $args[‘meta_key’] = ‘pa_stamp-year’;
    $args[‘orderby’] = ‘meta_value_num’;

    break;
    default:
    break;
    }
    }
    return $args;
    }

    add_filter( ‘woocommerce_default_catalog_orderby_options’, ‘wh_catalog_orderby’ );
    add_filter(‘woocommerce_catalog_orderby’, ‘wh_catalog_orderby’);

    function wh_catalog_orderby($sortby) {
    $sortby[‘pa_stamp-year’] = ‘Sort by year: Small – Large’;
    return $sortby;
    }

  6. Please update your code to this..

    your code saves the id of the custom attribute to post meta, not the value

    /**
    * Save product attributes to post metadata when a product is saved.
    *
    * @param int $post_id The post ID.
    * @param post $post The post object.
    * @param bool $update Whether this is an existing post being updated or not.
    *
    * Refrence: https://codex.wordpress.org/Plugin_API/Action_Reference/save_post
    */
    function wh_save_product_custom_meta($post_id, $post, $update) {
    $post_type = get_post_type($post_id);
    // If this isn’t a ‘product’ post, don’t update it.
    if ($post_type != ‘product’)
    return;

    $_product = wc_get_product($post_id);
    if(!$_product){
    return;
    }

    $mi_color = $_product->get_attribute( ‘pa_color’ );
    $mi_length = $_product->get_attribute( ‘pa_length’ );
    $mi_size = $_product->get_attribute( ‘pa_size’ );

    update_post_meta($_product->get_id(), ‘pa_size’, $mi_size);
    update_post_meta($_product->get_id(), ‘pa_length’, $mi_length);
    update_post_meta($_product->get_id(), ‘pa_color’, $mi_color);

    }
    add_action( ‘wp_insert_post’, ‘wh_save_product_custom_meta’, 10, 3);

  7. Hi, I tried to follow this procedure, but they are not working. Once asked for sorting, no product results. can you help me understand why?

    • Hi @Fernando,
      In this post I haven’t mentioned any code regarding saving. can you please elaborate as what you want to achieve. If you simply want to save all the array value you can use maybe_serialize() method.

      • I’m sorry if I misunderstood
        I have an attribute: Price M2
        In this line and by the code comment
        //it may have multiple colour (eg. black, brown, maroon, white) but we’ll take only the first colour.
        if (!empty($attribute_values[$key][0]) {
        update_post_meta($post_id, ‘pa_color’, $attribute_values[$key][0]);
        You are only saving the first key i.e. black, you are not saving all 4 values you mention. I would need to save all values,
        My page is as follows: https://www.vigorita.com.ar/categoria-producto/pisos-flotantes/page/2/
        And as you can see the prices per m2 are not sorted.

  8. Dear Raunak
    Thank you for this useful code. Is there any way to save product attributes to post metadata without saving the products? I have 500 imported products and if I don’t save them manually the filters doesn’t work.
    Can you help me please?

    • Hi @Sarah,

      Yes, it is achievable by writing one small script! You have to read the CSV file and add the data in postmeta table.

Leave a Reply to JohannaCancel Reply

Your email address will not be published. Required fields are marked *