Home > Software design >  How to order WooCommerce products by attribute term menu order
How to order WooCommerce products by attribute term menu order

Time:10-07

Let's say there's a product attribute "brand" with the following terms:

  • Brand A
  • Brand B
  • Brand C

Let's also say the menu order of these terms in products > attributes > size have been ordered as such:

  • Brand C
  • Brand A
  • Brand B

How can I sort the products on the shop page by the attribute menu order? So for example:

  • Brand C product
  • Brand A product
  • Brand B product

Assume each product only has a single brand attribute term.

So far I've figured the code will likely involve modifying the query using the pre_get_posts hook, but I'm having difficulty customizing the order in such a way.

CodePudding user response:

Since get_terms() is returning the order we have set for our attributes we can get those ids and use them as sorting.

//Change taxonomy if you need.
$terms = get_terms( array('taxonomy' => 'pa_brand','fields' => 'ids'));

From there we can use woocommerce_product_query to modify our query.

And finaly set the order of our query to be by term_id ASC or DESC.

Here is my solution

function sort_products_by_brand( $q ) {
        $terms = get_terms( array('taxonomy' => 'pa_brand','fields' => 'ids'));
        $tax_query = $q->get('tax_query');
        $tax_query[] = array(
            'taxonomy'  => 'pa_brand',
            'field'     => 'term_id',
            'terms'     => $terms,
        );

        $q->set( 'tax_query', $tax_query);
        $q->set( 'orderby', 'term_id' );
        // $q->set( 'order', 'DESC' ); // by default is ASC so uncomment if you want DESC.
    }
add_action( 'woocommerce_product_query', 'sort_products_by_brand' );

CodePudding user response:

Honk31's answer worked well for me. I just had to modify it a little bit.

add_action( 'pre_get_posts', 'jwd_modify_product_query' );
function jwd_modify_product_query($query) {
  if ( !is_admin() && $query->is_main_query() && is_post_type_archive( 'product' ) ) {
    $query->set( 'orderby', 'pa_brand' );
  }
}



// https://wordpress.stackexchange.com/a/363654/94213
// Answer by honk31
add_filter('posts_clauses', 'jwd_orderby_tax_clauses', 10, 2 );
function jwd_orderby_tax_clauses($clauses, $wp_query) {
  global $wpdb;
  $orderby = isset($wp_query->query_vars['orderby']) ? $wp_query->query_vars['orderby'] : false;

  if ($orderby && $orderby === 'pa_brand') {
    $clauses['join'] .= <<<SQL
    LEFT OUTER JOIN {$wpdb->term_relationships} ON {$wpdb->posts}.ID={$wpdb->term_relationships}.object_id
    LEFT OUTER JOIN {$wpdb->term_taxonomy} USING (term_taxonomy_id)
    LEFT OUTER JOIN {$wpdb->terms} USING (term_id)
    SQL;
    $clauses['where'] .= " AND (taxonomy = '{$orderby}' OR taxonomy IS NULL)";
    $clauses['groupby'] = "object_id";
    $clauses['orderby'] = "{$wpdb->terms}.term_order ASC";
    $clauses['orderby'] .= ", {$wpdb->posts}.post_name ASC";
  }

  return $clauses;
}

Another step that is needed is to use the Simple Custom Post Order plugin. The plugin adds a term_order column to the terms table that the code uses to sort by. Drag and drop to change the terms order in products > attributes > brand. I actually had to move all of the terms around to get the order to sort correctly.

WooCommerce attributes do have a drag & drop functionality already, though. Perhaps it is possible to do something similar without that plugin.

  • Related