Show Out of stock products at the end in Woocommerce

Is it possible to show out of stock products at the end of a category or page in wordpress?

So the customer first see the products that are available and after that the products that are out of stock.

Related posts

Leave a Reply

11 comments

  1. This is the same as Viktor & Bogdan’s answer, but without the extra Class code.

    It uses the post_clause filter to modify the product query. We JOIN the wp_postmeta table to the query and prepend an orderby _stock_status clause to the existing query. This way any other orderby clauses remain in the query as well.

    add_filter('posts_clauses', 'order_by_stock_status');
    function order_by_stock_status($posts_clauses) {
        global $wpdb;
        // only change query on WooCommerce loops
        if (is_woocommerce() && (is_shop() || is_product_category() || is_product_tag() || is_product_taxonomy())) {
            $posts_clauses['join'] .= " INNER JOIN $wpdb->postmeta istockstatus ON ($wpdb->posts.ID = istockstatus.post_id) ";
            $posts_clauses['orderby'] = " istockstatus.meta_value ASC, " . $posts_clauses['orderby'];
            $posts_clauses['where'] = " AND istockstatus.meta_key = '_stock_status' AND istockstatus.meta_value <> '' " . $posts_clauses['where'];
        }
        return $posts_clauses;
    }
    

    You could change istockstatus.meta_value ASC to istockstatus.meta_value DESC if you for some reason wanted the Out Of Stock items first.

    Tested on WP: 4.8; WC 3.0.8

  2. I tried all the solutions above, they worked but resulted in other issues on the site (probably due to theme conflict), Im sure they are all good in different situations / themes. The below code finally worked great for me (source mentioned)

    source: https://www.businessbloomer.com/woocommerce-show-in-stock-products-first-shop/

    /**
     * @snippet       Sort Products By Stock Status - WooCommerce Shop
     * @how-to        Get CustomizeWoo.com FREE
     * @author        Rodolfo Melogli
     * @compatible    WooCommerce 3.9
     * @donate $9     https://businessbloomer.com/bloomer-armada/
     */
     
    add_filter( 'woocommerce_get_catalog_ordering_args', 'bbloomer_first_sort_by_stock_amount', 9999 );
     
    function bbloomer_first_sort_by_stock_amount( $args ) {
       $args['orderby'] = 'meta_value';
       $args['order'] = 'ASC';
       $args['meta_key'] = '_stock_status';
       return $args;
    }
    
  3. Here is a snippet for rearranging products (in stock come first):

    <?php
    
    /**
     * Order product collections by stock status, instock products first.
     */
    class iWC_Orderby_Stock_Status
    {
    
        public function __construct()
        {
            // Check if WooCommerce is active
            if (in_array('woocommerce/woocommerce.php', apply_filters('active_plugins', get_option('active_plugins')))) {
                add_filter('posts_clauses', array($this, 'order_by_stock_status'), 2000);
            }
        }
    
        public function order_by_stock_status($posts_clauses)
        {
            global $wpdb;
            // only change query on WooCommerce loops
            if (is_woocommerce() && (is_shop() || is_product_category() || is_product_tag())) {
                $posts_clauses['join'] .= " INNER JOIN $wpdb->postmeta istockstatus ON ($wpdb->posts.ID = istockstatus.post_id) ";
                $posts_clauses['orderby'] = " istockstatus.meta_value ASC, " . $posts_clauses['orderby'];
                $posts_clauses['where'] = " AND istockstatus.meta_key = '_stock_status' AND istockstatus.meta_value <> '' " . $posts_clauses['where'];
            }
            return $posts_clauses;
        }
    }
    
    new iWC_Orderby_Stock_Status;
    
    ?>
    

    https://www.snip2code.com/Snippet/114858/WooCommerce-Products-Order-by-Stock-Stat

  4. @dacrosby answer really should be the best answer here. I’ve had a few issues with compatibility with some plugins but in most use cases it seems to work with no issues at all.

    That being said here are a few caveats

    • Plugins that create products programmatically can sometimes fail to add the stock status meta key and if this key doesn’t exist this solution will filter them out of the results entirely.
    • There are also use cases where this solution can conflict with existing ordering because we’re ordering alphabetically on the stockstatus value. If multiple stock status’ types are used they are artificially grouped in the results due to this ordering.

    This version built on top of the same implementation should provide wider compatibility without sacrificing the desired result.

    
    /**
     * Sorts the Woocommerce Archive product query to push out of stock products to the end 
     */
    function _nok_order_by_stock_status( $posts_clauses, $query ) {
    
        // only change query on WooCommerce loops
        if ( $query->is_main_query() && ( is_product_category() || is_product_tag() || is_product_taxonomy() || is_shop() ) ) {
            global $wpdb;
    
            $posts_clauses['join'] .= 
            " LEFT JOIN ( 
                SELECT post_id, meta_id, meta_value FROM $wpdb->postmeta 
                WHERE meta_key = '_stock_status' AND meta_value <> '' 
            ) istockstatus ON ($wpdb->posts.ID = istockstatus.post_id) ";
    
            $posts_clauses['orderby'] = 
            " CASE istockstatus.meta_value WHEN 
                'outofstock' THEN 1 
                ELSE 0 
            END ASC, " . $posts_clauses['orderby'];
        }
    
        return $posts_clauses;
    }
    add_filter( 'posts_clauses', '_nok_order_by_stock_status', 2000, 2 );
    
    
  5. Access the global configuration options for inventory management in WooCommerce, look to the left of your WordPress admin and click on WooCommerce, then on Settings, then click on the Inventory tab.

    You will find this “Out of Stock Visibility”

    Out of Stock Visibility – This checkbox will allow you to determine if you want to hide out of inventory items within the WooCommerce catalog.

    http://www.inmotionhosting.com/support/website/woocommerce/managing-inventory-in-woocommerce

    For making them appear at the end of the category you could use pre_get_posts to order based on the stock, but then you’ll lose your other sorting.

  6. Try this code (put in functions.php of your theme):

    class iWC_Orderby_Stock_Status {
    public function __construct() {
        if (in_array('woocommerce/woocommerce.php', apply_filters('active_plugins', get_option('active_plugins')))) {
            add_filter('posts_clauses', array($this, 'order_by_stock_status'), 2000);
        }
    }
    public function order_by_stock_status($posts_clauses) {
        global $wpdb;   
        if (is_woocommerce() && (is_shop() || is_product_category() || is_product_tag())) {
            $posts_clauses['join'] .= " INNER JOIN $wpdb->postmeta istockstatus ON ($wpdb->posts.ID = istockstatus.post_id) ";
            $posts_clauses['orderby'] = " istockstatus.meta_value ASC, " . $posts_clauses['orderby'];
            $posts_clauses['where'] = " AND istockstatus.meta_key = '_stock_status' AND istockstatus.meta_value <> '' " . $posts_clauses['where'];
        }
        return $posts_clauses;
        }
    }
    new iWC_Orderby_Stock_Status;
    

    Taken here.

    For the last versions of WooCommerce see answer below: https://stackoverflow.com/a/44597448/3925099

  7. This is the best solution:

    add_action( 'pre_get_posts', function ( $q ) {
        if (   !is_admin()                 // Target only front end 
             && $q->is_main_query()        // Only target the main query
             && $q->is_post_type_archive() // Change to suite your needs
        ) {
            $q->set( 'meta_key', '_stock_status' );
            $q->set( 'orderby',  'meta_value'    );
            $q->set( 'order',    'ASC'           );
        }
    }, PHP_INT_MAX );
    
  8. This code work for me:

    add_action( 'pre_get_posts', function( $query ) {
        if ( $query->is_main_query() && is_woocommerce() && ( is_shop() || is_product_category() || is_product_tag() ) ) {
            if( $query->get( 'orderby' ) == 'menu_order title' ) {  // only change default sorting
                $query->set( 'orderby', 'meta_value' );
                $query->set( 'order', 'ASC' );
                $query->set( 'meta_key', '_stock_status' );
            }
        }
    });
    
  9. Edited one answer above:

    /**
     * @snippet     Order product collections by stock status, instock products first.
     * @author      Rkoms
     */
    
    class iWC_Orderby_Stock_Status {
    
        public function __construct() {
            if (in_array('woocommerce/woocommerce.php', apply_filters('active_plugins', get_option('active_plugins')))) {
                add_filter('posts_clauses', array($this, 'order_by_stock_status'), 2000, 2);
            }
        }
    
        public function order_by_stock_status($posts_clauses, $query) {
            global $wpdb;   
            if ( $query->is_main_query() && is_woocommerce() && (is_shop() || is_product_category() || is_product_tag())) {
                $posts_clauses['join'] .= " INNER JOIN $wpdb->postmeta istockstatus ON ($wpdb->posts.ID = istockstatus.post_id) ";
                $posts_clauses['orderby'] = " istockstatus.meta_value ASC, " . $posts_clauses['orderby'];
                $posts_clauses['where'] = " AND istockstatus.meta_key = '_stock_status' AND istockstatus.meta_value <> '' " . $posts_clauses['where'];
            }
            return $posts_clauses;
        }
    
    }
    new iWC_Orderby_Stock_Status;
    
  10. As an alternative, simple solution, if your product list is in a flexbox, you could do it with CSS too with the following rule:

    ul.products li.product.outofstock {order:1}
    
  11. I think this query can be better, because if a site sells its products by pre-order, it will be displayed at the end of the list.
    This query is the optimized version that you can use.
    Also, pre-order products will remain in place:

    function custom_product_query( $clauses, $query ) {
        global $wpdb;
        $clauses['join'] .= "
            LEFT JOIN {$wpdb->prefix}postmeta out_of_stock_meta
            ON ({$wpdb->prefix}posts.ID = out_of_stock_meta.post_id
                AND out_of_stock_meta.meta_key = '_stock_status'
                AND out_of_stock_meta.meta_value = 'outofstock')
        ";
        $clauses['orderby'] = "out_of_stock_meta.meta_value ASC, {$clauses['orderby']}";
        return $clauses;
    }
    add_filter( 'posts_clauses', 'custom_product_query', 10, 2 );
    
    

    This code adds a left join to the postmeta table to retrieve the _stock_status meta value, and orders the results first by whether the product is out of stock, and then by the original orderby value. This filter only applies to product category queries, and won’t affect other queries.

    This approach should be relatively efficient because it doesn’t require an additional query, but rather modifies the existing query with a small additional join and sort.