* @return boolean */ private function should_adjust_price_filters_for_displayed_taxes() { $display = get_option( 'woocommerce_tax_display_shop' ); $database = wc_prices_include_tax() ? 'incl' : 'excl'; return $display !== $database; } /** * Get query for price filters when dealing with displayed taxes. * * @param float $price_filter Price filter to apply. * @param string $column Price being filtered (min or max). * @param string $operator Comparison operator for column. Accepts '>=' or '<='. * @return string Constructed query. */ private function get_price_filter_query_for_displayed_taxes( $price_filter, $column = 'min_price', $operator = '>=' ) { global $wpdb; if ( ! in_array( $operator, array( '>=', '<=' ), true ) ) { return ''; } // Select only used tax classes to avoid unwanted calculations. $cache_key = WC_Cache_Helper::get_cache_prefix( 'filter_clauses' ) . 'tax_classes'; $product_tax_classes = wp_cache_get( $cache_key ); if ( ! $product_tax_classes ) { $product_tax_classes = $wpdb->get_col( "SELECT DISTINCT tax_class FROM {$wpdb->wc_product_meta_lookup};" ); wp_cache_set( $cache_key, $product_tax_classes ); } if ( empty( $product_tax_classes ) ) { return ''; } $or_queries = array(); // We need to adjust the filter for each possible tax class and combine the queries into one. foreach ( $product_tax_classes as $tax_class ) { $adjusted_price_filter = $this->adjust_price_filter_for_tax_class( $price_filter, $tax_class ); $or_queries[] = $wpdb->prepare( '( wc_product_meta_lookup.tax_class = %s AND wc_product_meta_lookup.`' . esc_sql( $column ) . '` ' . esc_sql( $operator ) . ' %f )', $tax_class, $adjusted_price_filter ); } // phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQL.NotPrepared return $wpdb->prepare( ' AND ( wc_product_meta_lookup.tax_status = "taxable" AND ( 0=1 OR ' . implode( ' OR ', $or_queries ) . ') OR ( wc_product_meta_lookup.tax_status != "taxable" AND wc_product_meta_lookup.`' . esc_sql( $column ) . '` ' . esc_sql( $operator ) . ' %f ) ) ', $price_filter ); // phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQL.NotPrepared } /** * Adjusts a price filter based on a tax class and whether or not the amount includes or excludes taxes. * * This calculation logic is based on `wc_get_price_excluding_tax` and `wc_get_price_including_tax` in core. * * @param float $price_filter Price filter amount as entered. * @param string $tax_class Tax class for adjustment. * @return float */ private function adjust_price_filter_for_tax_class( $price_filter, $tax_class ) { $tax_display = get_option( 'woocommerce_tax_display_shop' ); $tax_rates = WC_Tax::get_rates( $tax_class ); $base_tax_rates = WC_Tax::get_base_tax_rates( $tax_class ); // If prices are shown incl. tax, we want to remove the taxes from the filter amount to match prices stored excl. tax. if ( 'incl' === $tax_display ) { /** * Filters if taxes should be removed from locations outside the store base location. * * The woocommerce_adjust_non_base_location_prices filter can stop base taxes being taken off when dealing * with out of base locations. e.g. If a product costs 10 including tax, all users will pay 10 * regardless of location and taxes. * * @since 2.6.0 * * @internal Matches filter name in WooCommerce core. * * @param boolean $adjust_non_base_location_prices True by default. * @return boolean */ $taxes = apply_filters( 'woocommerce_adjust_non_base_location_prices', true ) ? WC_Tax::calc_tax( $price_filter, $base_tax_rates, true ) : WC_Tax::calc_tax( $price_filter, $tax_rates, true ); return $price_filter - array_sum( $taxes ); } // If prices are shown excl. tax, add taxes to match the prices stored in the DB. $taxes = WC_Tax::calc_tax( $price_filter, $tax_rates, false ); return $price_filter + array_sum( $taxes ); } /** * Get an array of attributes and terms selected from query arguments. * * @param array $query_vars The WP_Query arguments. * @return array */ private function get_chosen_attributes( $query_vars ) { $chosen_attributes = array(); if ( empty( $query_vars ) ) { return $chosen_attributes; } foreach ( $query_vars as $key => $value ) { if ( 0 === strpos( $key, 'filter_' ) ) { $attribute = wc_sanitize_taxonomy_name( str_replace( 'filter_', '', $key ) ); $taxonomy = wc_attribute_taxonomy_name( $attribute ); $filter_terms = ! empty( $value ) ? explode( ',', wc_clean( wp_unslash( $value ) ) ) : array(); if ( empty( $filter_terms ) || ! taxonomy_exists( $taxonomy ) || ! wc_attribute_taxonomy_id_by_name( $attribute ) ) { continue; } $query_type = ! empty( $query_vars[ 'query_type_' . $attribute ] ) && in_array( $query_vars[ 'query_type_' . $attribute ], array( 'and', 'or' ), true ) ? wc_clean( wp_unslash( $query_vars[ 'query_type_' . $attribute ] ) ) : ''; $chosen_attributes[ $taxonomy ]['terms'] = array_map( 'sanitize_title', $filter_terms ); // Ensures correct encoding. $chosen_attributes[ $taxonomy ]['query_type'] = $query_type ? $query_type : 'and'; } } return $chosen_attributes; } /** * Get attribute lookup table name. * * @return string */ private function get_lookup_table_name() { return wc_get_container()->get( LookupDataStore::class )->get_lookup_table_name(); } }