oin( $clauses['join'] ); $is_ascending_order = 'asc' === strtolower( $query_vars['order'] ?? 'desc' ); $clauses['orderby'] = $is_ascending_order ? 'wc_product_meta_lookup.min_price ASC, wc_product_meta_lookup.product_id ASC' : 'wc_product_meta_lookup.max_price DESC, wc_product_meta_lookup.product_id DESC'; return $clauses; } /** * Add the `posts_clauses` filter to add sales-based sorting * * @param array $clauses The list of clauses for the query. * @param WP_Query $query The WP_Query instance. * @return array Modified list of clauses. */ public function add_sales_sorting_posts_clauses( $clauses, $query ) { $query_vars = $query->query_vars; $is_product_collection_block = $query_vars['isProductCollection'] ?? false; if ( ! $is_product_collection_block ) { return $clauses; } $orderby = $query_vars['orderby'] ?? null; // The popularity orderby value here is for backwards compatibility as we have since removed the filter option. if ( 'sales' !== $orderby && 'popularity' !== $orderby ) { return $clauses; } $clauses['join'] = $this->append_product_sorting_table_join( $clauses['join'] ); $is_ascending_order = 'asc' === strtolower( $query_vars['order'] ?? 'desc' ); $clauses['orderby'] = $is_ascending_order ? 'wc_product_meta_lookup.total_sales ASC, wc_product_meta_lookup.product_id ASC' : 'wc_product_meta_lookup.total_sales DESC, wc_product_meta_lookup.product_id DESC'; return $clauses; } /** * Join wc_product_meta_lookup to posts if not already joined. * * @param string $sql SQL join. * @return string */ protected function append_product_sorting_table_join( $sql ) { global $wpdb; if ( ! strstr( $sql, 'wc_product_meta_lookup' ) ) { $sql .= " LEFT JOIN {$wpdb->wc_product_meta_lookup} wc_product_meta_lookup ON $wpdb->posts.ID = wc_product_meta_lookup.product_id "; } return $sql; } /** * Merge all of the 'post__in' values and return an array containing only values that are present in all arrays. * * @param int[][] ...$post__in The 'post__in' values to be merged. * * @return int[] The merged 'post__in' values. */ private function merge_post__in( ...$post__in ) { if ( empty( $post__in ) ) { return array(); } // Since we're using array_intersect, any array that is empty will result // in an empty output array. To avoid this we need to make sure every // argument is a non-empty array. $post__in = array_filter( $post__in, function ( $val ) { return is_array( $val ) && ! empty( $val ); } ); if ( empty( $post__in ) ) { return array(); } // Since the 'post__in' filter is exclusionary we need to use an intersection of // all of the arrays. This ensures one query doesn't add options that another // has otherwise excluded from the results. if ( count( $post__in ) > 1 ) { $post__in = array_intersect( ...$post__in ); // An empty array means that there was no overlap between the filters and so // the query should return no results. if ( empty( $post__in ) ) { return array( -1 ); } } else { $post__in = reset( $post__in ); } return array_values( array_unique( $post__in, SORT_NUMERIC ) ); } /** * Add the `posts_clauses` filter to add menu order with title fallback sorting * * @param array $clauses The list of clauses for the query. * @param WP_Query $query The WP_Query instance. * @return array Modified list of clauses. */ public function add_menu_order_with_title_fallback_posts_clauses( $clauses, $query ) { $query_vars = $query->query_vars; $is_product_collection_block = $query_vars['isProductCollection'] ?? false; if ( ! $is_product_collection_block ) { return $clauses; } $orderby = $query_vars['orderby'] ?? null; if ( 'menu_order' !== $orderby ) { return $clauses; } $is_ascending_order = ! isset( $query_vars['order'] ) || 'asc' === strtolower( $query_vars['order'] ); $clauses['orderby'] = $is_ascending_order ? 'menu_order ASC, post_title ASC' : 'menu_order DESC, post_title DESC'; return $clauses; } }