class-wc-widget-brand-description.php
1 year ago
class-wc-widget-brand-nav.php
7 months ago
class-wc-widget-brand-thumbnails.php
1 year ago
class-wc-widget-cart.php
3 years ago
class-wc-widget-layered-nav-filters.php
2 years ago
class-wc-widget-layered-nav.php
3 months ago
class-wc-widget-price-filter.php
8 months ago
class-wc-widget-product-categories.php
7 months ago
class-wc-widget-product-search.php
5 years ago
class-wc-widget-product-tag-cloud.php
5 years ago
class-wc-widget-products.php
1 year ago
class-wc-widget-rating-filter.php
5 years ago
class-wc-widget-recent-reviews.php
2 months ago
class-wc-widget-recently-viewed.php
1 year ago
class-wc-widget-top-rated-products.php
5 years ago
class-wc-widget-price-filter.php
190 lines
| 1 | <?php |
| 2 | /** |
| 3 | * Price Filter Widget and related functions. |
| 4 | * |
| 5 | * Generates a range slider to filter products by price. |
| 6 | * |
| 7 | * @package WooCommerce\Widgets |
| 8 | * @version 2.3.0 |
| 9 | */ |
| 10 | |
| 11 | use Automattic\Jetpack\Constants; |
| 12 | |
| 13 | defined( 'ABSPATH' ) || exit; |
| 14 | |
| 15 | /** |
| 16 | * Widget price filter class. |
| 17 | */ |
| 18 | class WC_Widget_Price_Filter extends WC_Widget { |
| 19 | |
| 20 | /** |
| 21 | * Constructor. |
| 22 | */ |
| 23 | public function __construct() { |
| 24 | $this->widget_cssclass = 'woocommerce widget_price_filter'; |
| 25 | $this->widget_description = __( 'Display a slider to filter products in your store by price.', 'woocommerce' ); |
| 26 | $this->widget_id = 'woocommerce_price_filter'; |
| 27 | $this->widget_name = __( 'Filter Products by Price', 'woocommerce' ); |
| 28 | $this->settings = array( |
| 29 | 'title' => array( |
| 30 | 'type' => 'text', |
| 31 | 'std' => __( 'Filter by price', 'woocommerce' ), |
| 32 | 'label' => __( 'Title', 'woocommerce' ), |
| 33 | ), |
| 34 | ); |
| 35 | $suffix = Constants::is_true( 'SCRIPT_DEBUG' ) ? '' : '.min'; |
| 36 | $version = Constants::get_constant( 'WC_VERSION' ); |
| 37 | wp_register_script( 'wc-accounting', WC()->plugin_url() . '/assets/js/accounting/accounting' . $suffix . '.js', array( 'jquery' ), '0.4.2', true ); |
| 38 | // This script is deprecated, but we need to register it for backwards compatibility. |
| 39 | // This can be removed in WooCommerce 10.5.0. |
| 40 | wp_register_script( 'accounting', false, array( 'wc-accounting' ), '0.4.2', true ); |
| 41 | wp_register_script( 'wc-jquery-ui-touchpunch', WC()->plugin_url() . '/assets/js/jquery-ui-touch-punch/jquery-ui-touch-punch' . $suffix . '.js', array( 'jquery-ui-slider' ), $version, true ); |
| 42 | wp_register_script( 'wc-price-slider', WC()->plugin_url() . '/assets/js/frontend/price-slider' . $suffix . '.js', array( 'jquery-ui-slider', 'wc-jquery-ui-touchpunch', 'wc-accounting' ), $version, true ); |
| 43 | wp_localize_script( |
| 44 | 'wc-price-slider', |
| 45 | 'woocommerce_price_slider_params', |
| 46 | array( |
| 47 | 'currency_format_num_decimals' => 0, |
| 48 | 'currency_format_symbol' => get_woocommerce_currency_symbol(), |
| 49 | 'currency_format_decimal_sep' => esc_attr( wc_get_price_decimal_separator() ), |
| 50 | 'currency_format_thousand_sep' => esc_attr( wc_get_price_thousand_separator() ), |
| 51 | 'currency_format' => esc_attr( str_replace( array( '%1$s', '%2$s' ), array( '%s', '%v' ), get_woocommerce_price_format() ) ), |
| 52 | ) |
| 53 | ); |
| 54 | |
| 55 | if ( is_customize_preview() ) { |
| 56 | wp_enqueue_script( 'wc-price-slider' ); |
| 57 | } |
| 58 | |
| 59 | parent::__construct(); |
| 60 | } |
| 61 | |
| 62 | /** |
| 63 | * Output widget. |
| 64 | * |
| 65 | * @see WP_Widget |
| 66 | * |
| 67 | * @param array $args Arguments. |
| 68 | * @param array $instance Widget instance. |
| 69 | */ |
| 70 | public function widget( $args, $instance ) { |
| 71 | global $wp; |
| 72 | |
| 73 | // Requires lookup table added in 3.6. |
| 74 | if ( version_compare( get_option( 'woocommerce_db_version', null ), '3.6', '<' ) ) { |
| 75 | return; |
| 76 | } |
| 77 | |
| 78 | if ( ! is_shop() && ! is_product_taxonomy() ) { |
| 79 | return; |
| 80 | } |
| 81 | |
| 82 | // If there are not posts and we're not filtering, hide the widget. |
| 83 | if ( ! WC()->query->get_main_query()->post_count && ! isset( $_GET['min_price'] ) && ! isset( $_GET['max_price'] ) ) { // WPCS: input var ok, CSRF ok. |
| 84 | return; |
| 85 | } |
| 86 | |
| 87 | wp_enqueue_script( 'wc-price-slider' ); |
| 88 | |
| 89 | // Round values to nearest 10 by default. |
| 90 | $step = max( apply_filters( 'woocommerce_price_filter_widget_step', 10 ), 1 ); |
| 91 | |
| 92 | // Find min and max price in current result set. |
| 93 | $prices = $this->get_filtered_price(); |
| 94 | $min_price = $prices->min_price; |
| 95 | $max_price = $prices->max_price; |
| 96 | |
| 97 | // Check to see if we should add taxes to the prices if store are excl tax but display incl. |
| 98 | $tax_display_mode = get_option( 'woocommerce_tax_display_shop' ); |
| 99 | |
| 100 | if ( wc_tax_enabled() && ! wc_prices_include_tax() && 'incl' === $tax_display_mode ) { |
| 101 | $tax_class = apply_filters( 'woocommerce_price_filter_widget_tax_class', '' ); // Uses standard tax class. |
| 102 | $tax_rates = WC_Tax::get_rates( $tax_class ); |
| 103 | |
| 104 | if ( $tax_rates ) { |
| 105 | $min_price += WC_Tax::get_tax_total( WC_Tax::calc_exclusive_tax( $min_price, $tax_rates ) ); |
| 106 | $max_price += WC_Tax::get_tax_total( WC_Tax::calc_exclusive_tax( $max_price, $tax_rates ) ); |
| 107 | } |
| 108 | } |
| 109 | |
| 110 | $min_price = apply_filters( 'woocommerce_price_filter_widget_min_amount', floor( $min_price / $step ) * $step ); |
| 111 | $max_price = apply_filters( 'woocommerce_price_filter_widget_max_amount', ceil( $max_price / $step ) * $step ); |
| 112 | |
| 113 | // If both min and max are equal, we don't need a slider. |
| 114 | if ( $min_price === $max_price ) { |
| 115 | return; |
| 116 | } |
| 117 | |
| 118 | $current_min_price = isset( $_GET['min_price'] ) ? floor( floatval( wp_unslash( $_GET['min_price'] ) ) / $step ) * $step : $min_price; // WPCS: input var ok, CSRF ok. |
| 119 | $current_max_price = isset( $_GET['max_price'] ) ? ceil( floatval( wp_unslash( $_GET['max_price'] ) ) / $step ) * $step : $max_price; // WPCS: input var ok, CSRF ok. |
| 120 | |
| 121 | $this->widget_start( $args, $instance ); |
| 122 | |
| 123 | if ( '' === get_option( 'permalink_structure' ) ) { |
| 124 | $form_action = remove_query_arg( array( 'page', 'paged', 'product-page' ), add_query_arg( $wp->query_string, '', home_url( $wp->request ) ) ); |
| 125 | } else { |
| 126 | $form_action = preg_replace( '%\/page/[0-9]+%', '', home_url( trailingslashit( $wp->request ) ) ); |
| 127 | } |
| 128 | |
| 129 | wc_get_template( |
| 130 | 'content-widget-price-filter.php', |
| 131 | array( |
| 132 | 'form_action' => $form_action, |
| 133 | 'step' => $step, |
| 134 | 'min_price' => $min_price, |
| 135 | 'max_price' => $max_price, |
| 136 | 'current_min_price' => $current_min_price, |
| 137 | 'current_max_price' => $current_max_price, |
| 138 | ) |
| 139 | ); |
| 140 | |
| 141 | $this->widget_end( $args ); |
| 142 | } |
| 143 | |
| 144 | /** |
| 145 | * Get filtered min price for current products. |
| 146 | * |
| 147 | * @return int |
| 148 | */ |
| 149 | protected function get_filtered_price() { |
| 150 | global $wpdb; |
| 151 | |
| 152 | $args = WC()->query->get_main_query()->query_vars; |
| 153 | $tax_query = isset( $args['tax_query'] ) ? $args['tax_query'] : array(); |
| 154 | $meta_query = isset( $args['meta_query'] ) ? $args['meta_query'] : array(); |
| 155 | |
| 156 | if ( ! is_post_type_archive( 'product' ) && ! empty( $args['taxonomy'] ) && ! empty( $args['term'] ) ) { |
| 157 | $tax_query[] = WC()->query->get_main_tax_query(); |
| 158 | } |
| 159 | |
| 160 | foreach ( $meta_query + $tax_query as $key => $query ) { |
| 161 | if ( ! empty( $query['price_filter'] ) || ! empty( $query['rating_filter'] ) ) { |
| 162 | unset( $meta_query[ $key ] ); |
| 163 | } |
| 164 | } |
| 165 | |
| 166 | $meta_query = new WP_Meta_Query( $meta_query ); |
| 167 | $tax_query = new WP_Tax_Query( $tax_query ); |
| 168 | $search = WC_Query::get_main_search_query_sql(); |
| 169 | |
| 170 | $meta_query_sql = $meta_query->get_sql( 'post', $wpdb->posts, 'ID' ); |
| 171 | $tax_query_sql = $tax_query->get_sql( $wpdb->posts, 'ID' ); |
| 172 | $search_query_sql = $search ? ' AND ' . $search : ''; |
| 173 | |
| 174 | $sql = " |
| 175 | SELECT min( min_price ) as min_price, MAX( max_price ) as max_price |
| 176 | FROM {$wpdb->wc_product_meta_lookup} |
| 177 | WHERE product_id IN ( |
| 178 | SELECT ID FROM {$wpdb->posts} |
| 179 | " . $tax_query_sql['join'] . $meta_query_sql['join'] . " |
| 180 | WHERE {$wpdb->posts}.post_type IN ('" . implode( "','", array_map( 'esc_sql', apply_filters( 'woocommerce_price_filter_post_type', array( 'product' ) ) ) ) . "') |
| 181 | AND {$wpdb->posts}.post_status = 'publish' |
| 182 | " . $tax_query_sql['where'] . $meta_query_sql['where'] . $search_query_sql . ' |
| 183 | )'; |
| 184 | |
| 185 | $sql = apply_filters( 'woocommerce_price_filter_sql', $sql, $meta_query_sql, $tax_query_sql ); |
| 186 | |
| 187 | return $wpdb->get_row( $sql ); // WPCS: unprepared SQL ok. |
| 188 | } |
| 189 | } |
| 190 |