Automatically moves out-of-stock products to the end of WooCommerce shop and archive pages. Unlike basic sorting approaches, this snippet uses a SQL JOIN to preserve the existing sort order (price, popularity, date, etc.) while only pushing out-of-stock items to the bottom.
add_filter( 'posts_clauses', function( $clauses, $query ) {
if ( is_admin() || ! $query->is_main_query() || ! is_woocommerce() ) {
return $clauses;
}
global $wpdb;
$clauses['join'] .= " LEFT JOIN {$wpdb->postmeta} AS stock_meta
ON ({$wpdb->posts}.ID = stock_meta.post_id
AND stock_meta.meta_key = '_stock_status')";
$clauses['orderby'] = "CASE WHEN stock_meta.meta_value = 'outofstock' THEN 1 ELSE 0 END ASC, " . $clauses['orderby'];
return $clauses;
}, 10, 2 );