skip to Main Content

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.

2

Answers


  1. Chosen as BEST ANSWER

    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.


  2. 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' );
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search