Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /**
- * Sorts products by price, using their currency-specific prices.
- *
- * HOW TO USE THIS CODE
- * This code shows how to override the "sort by price" logic used by WooCommerce, to ensure that products
- * are sorted using their currency-specific price.
- *
- * DISCLAIMER
- * THE USE OF THIS CODE IS AT YOUR OWN RISK. You remain fully liable for compliance with tax laws.
- * This code is offered free of charge and there is no warranty for it, to the extent permitted by applicable law.
- * Except when otherwise stated in writing the copyright holders and/or other parties provide the program "as is"
- * without warranty of any kind, either expressed or implied, including, but not limited to, the implied warranties of
- * merchantability and fitness for a particular purpose. The entire risk as to the quality and performance of the program
- * is with you. Should the program prove defective, you assume the cost of all necessary servicing, repair or correction.
- *
- * The code is provided as an example and it's not covered by our support service. We won't be able to offer free support
- * in relation to it. Should you need a consultation, or assistance to customise this code, you can contact us to avail
- * of our paid consultation services: https://aelia.co/hire_us
- */
- class Aelia_Live_Product_Sort {
- protected static $query_order = '';
- protected static $query_limits = array();
- /**
- * Fetches the prices of all the products in the catalogue.
- *
- * @param string currency
- * @return array
- */
- protected static function fetch_product_prices(string $currency = null) {
- $transient_id = implode('_', array(
- 'wc_aelia_get_all_product_prices',
- $currency ?? get_woocommerce_currency(),
- WC_Cache_Helper::get_transient_version('product'),
- ));
- $product_prices = get_transient($transient_id);
- if($product_prices === false) {
- global $wpdb;
- $sql = "
- SELECT DISTINCT product_id
- FROM {$wpdb->wc_product_meta_lookup}
- ";
- $product_prices = array();
- foreach(wp_list_pluck($wpdb->get_results($sql), 'product_id') as $product_id) {
- $product = wc_get_product($product_id);
- // Skip invalid products and variations
- if(!$product instanceof \WC_Product || $product instanceof \WC_Product_Variation) {
- continue;
- }
- $product_prices[$product_id] = array();
- if($product instanceof \WC_Product_Variable) {
- $children_prices = array();
- // Fetch the price of each variation
- foreach($product->get_visible_children() as $child_product_id) {
- $children_prices[] = apply_filters('wc_aelia_cs_get_product_base_price_in_currency', get_post_meta('_price', $child_product_id, true), $child_product_id);
- }
- $product_prices[$product_id]['min'] = !empty($children_prices) ? min($children_prices) : '';
- $product_prices[$product_id]['max'] = !empty($children_prices) ? max($children_prices) : '';
- }
- else {
- $product_prices[$product_id]['max'] = $product_prices[$product_id]['min'] = apply_filters('wc_aelia_cs_get_product_base_price_in_currency', get_post_meta('_price', $product_id, true), $product_id);
- }
- }
- if(is_array($product_prices)) {
- // Cache the products, for performance
- set_transient($transient_id, $product_prices, HOUR_IN_SECONDS);
- }
- }
- return $product_prices;
- }
- /**
- * Given a "LIMIT x, y" clause, returns an array with the start and length.
- *
- * @param string $limits
- * @return array
- */
- protected static function extract_query_limits(string $limits): array {
- $limit_parts = explode(',', str_replace('LIMIT ', '', $limits));
- $limit_parts = array_filter(array_map(function($value) {
- $value = trim($value);
- return is_numeric($value) ? (int)$value : '';
- }, $limit_parts));
- if(empty($limit_parts)) {
- return array(
- 'start' => 0,
- 'length' => null,
- );
- }
- return array(
- 'start' => (count($limit_parts) > 1) ? array_shift($limit_parts) : 0,
- 'length' => array_shift($limit_parts),
- );
- }
- /**
- * Overrides the limits set against a WP_Query, so that it always returns
- * all the products.
- *
- * @param string $limits
- * @param WP_Query $query
- * @return string
- */
- public static function post_limits_request($limits, $query): string {
- self::$query_limits = self::extract_query_limits($limits);
- remove_filter('post_limits_request', array(__CLASS__, 'post_limits_request'), 10, 2);
- return '';
- }
- /**
- * Filters the list of posts returned by a WP_Query, doing the following:
- * - Sorts them by the applicable price in the active currency.
- * - Returns only the posts that fall into the LIMITS set for the query.
- *
- * @param array $posts
- * @param WP_Query $wp_query
- * @return array
- */
- public static function posts_results($posts, $wp_query) {
- remove_filter('posts_results', array(__CLASS__, 'posts_results'), 99, 2);
- $price_type = (self::$query_order === 'DESC') ? 'max' : 'min';
- // Fetch all the product prices
- $product_prices = wp_list_pluck(self::fetch_product_prices(), $price_type);
- // Extract the numeric prices
- $numeric_prices = array_filter($product_prices, 'is_numeric');
- // Sort the numeric prices in ascending or descending order
- if(self::$query_order === 'DESC') {
- arsort($numeric_prices, SORT_NUMERIC);
- }
- else {
- asort($numeric_prices, SORT_NUMERIC);
- }
- // Return a list of post, sorted as follows
- // - All the products with a numeric price, in ascending or descending order
- // - All the product with a non-numeric price, in any order
- $ordered_product_ids = array_flip(array_keys($numeric_prices + array_diff_key($product_prices, $numeric_prices)));
- uasort($posts, function($a, $b) use ($ordered_product_ids) {
- $a_pos = $ordered_product_ids[$a->ID] ?? -1;
- $b_pos = $ordered_product_ids[$b->ID] ?? -1;
- return $a_pos <=> $b_pos;
- });
- return array_slice($posts, self::$query_limits['start'], self::$query_limits['length']);
- }
- /**
- * Intercepts the ordering arguments set against a WC_Query, to enable the sorting
- * by price in active currency.
- *
- * @param array $args
- * @param string $orderby
- * @param string $order
- * @return array
- */
- public static function woocommerce_get_catalog_ordering_args($args, $orderby, $order) {
- if($orderby === 'price') {
- self::$query_order = $order;
- add_filter('post_limits_request', array(__CLASS__, 'post_limits_request'), 10, 2);
- add_filter('posts_results', array(__CLASS__, 'posts_results'), 99, 2);
- }
- return $args;
- }
- public static function init() {
- add_filter('woocommerce_get_catalog_ordering_args', array(__CLASS__, 'woocommerce_get_catalog_ordering_args'), 99, 3);
- }
- }
- Aelia_Live_Product_Sort::init();
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement