PluginProbe
Filter Everything — WordPress & WooCommerce Filters / 1.9.7
Filter Everything — WordPress & WooCommerce Filters v1.9.7
1.9.7 1.9.6 1.9.5 1.9.4 1.9.3 1.9.2.2 1.9.2.1 trunk 1.2.1 1.2.3 1.2.4 1.2.5 1.3.0 1.3.1 1.3.2 1.4.1 1.4.4 1.4.5 1.4.8 1.4.9 1.5.0 1.5.1 1.6.0 1.6.1 1.6.2 All 52 releases
← All changes | src/Entities/PostMetaNumEntity.php +378 -344 1.4.81.9.7 View file →
@@ -1,30 +1,49 @@
1 1 <?php
2 2
3 3 namespace FilterEverything\Filter;
4 4
5 -if ( ! defined('WPINC') ) {
6 - wp_die();
5 +if ( ! defined('ABSPATH') ) {
6 + exit;
7 7 }
8 8
9 9 class PostMetaNumEntity implements Entity
10 10 {
11 + use PostMetaTrait;
11 12 public $items = [];
13 + /**
14 + * Declared explicitly: assigned from the outside in
15 + * EntityManager::prepareEntitiesToDisplay(); dynamic properties are
16 + * deprecated since PHP 8.2.
17 + */
18 + public $items_sort = [];
12 19
20 + public $filter = [];
21 +
22 + protected $product_cache = [];
23 +
13 24 public $entityName = '';
14 25
15 26 public $excludedTerms = [];
16 27
28 + public $isInclude = false;
29 +
17 30 public $new_meta_query = [];
18 31
19 32 public $postTypes = [];
20 33
34 + public $wdr_product_ids = [];
35 +
36 + public $is_woo_discount_rules = false;
37 +
21 38 public function __construct( $postMetaName, $postType ){
22 39 /**
23 40 * @feature clean code from unused methods
24 41 */
25 -
26 42 $this->entityName = $postMetaName;
43 + if(flrt_is_woo_discount_rules()){
44 + $this->is_woo_discount_rules = true;
45 + }
27 46 $this->setPostTypes( array($postType) );
28 47 }
29 48
30 49 public function setPostTypes( $postTypes = false )
@@ -48,20 +67,17 @@
48 67
49 68 $this->getAllExistingTerms();
50 69 }
51 70
52 - public static function inputName( $metaKey, $edge = 'min' )
71 + public static function inputName( $slug, $edge = 'min' )
53 72 {
54 - if( mb_strpos( $metaKey, '_' ) === 0 ){
55 - return $edge . $metaKey;
56 - }
57 -
58 - return $edge . '_' . $metaKey;
73 + return $edge . '_' . $slug;
59 74 }
60 75
61 - public function setExcludedTerms( $excludedTerms )
76 + public function setExcludedTerms( $excludedTerms, $isInclude )
62 77 {
63 78 $this->excludedTerms = $excludedTerms;
79 + $this->isInclude = $isInclude;
64 80 }
65 81
66 82 public function getName()
67 83 {
@@ -69,26 +85,14 @@
69 85 }
70 86
71 87 function excludeTerms( $terms )
72 88 {
73 - $exclude = [];
74 -
75 - if( ! empty( $this->excludedTerms ) ){
76 - $exclude = $this->excludedTerms;
77 - }
78 -
79 - foreach( $terms as $index => $term ){
80 - if( in_array( $term->slug, $exclude ) ){
81 - unset( $terms[$index] );
82 - }
83 - }
84 -
85 89 return $terms;
86 90 }
87 91
88 92 function getTerms()
89 93 {
90 - return $this->excludeTerms( $this->getAllExistingTerms() );
94 + return $this->excludeTerms( $this->getAllExistingTerms() );
91 95 }
92 96
93 97 /**
94 98 * @param int $id term id
@@ -94,9 +98,8 @@
94 98 * @param int $id term id
95 99 * @return false|object term object of false
96 100 */
97 101 public function getTerm( $termId ){
98 -
99 102 if( ! $termId ){
100 103 return false;
101 104 }
102 105
@@ -144,13 +147,12 @@
144 147 {
145 148 if( empty( $this->items ) || $force ) {
146 149 $this->items = $this->selectTerms();
147 150 }
148 -
149 151 return $this->items;
150 152 }
151 153
152 - private function isDecimal( $step = 0, $value = 0 )
154 + public function isDecimal( $step = 0, $value = 0 )
153 155 {
154 156 if( strpos( $step, '.') !== false ){
155 157 return true;
156 158 }
@@ -159,160 +161,294 @@
159 161 return true;
160 162 }
161 163
162 164 return false;
163 -// return is_numeric( $value ) && floor( $value ) != $value;
164 165 }
165 166
166 - private function getTermPosts( $edge, $value )
167 - { global $wpdb;
168 - $postIds = [];
169 - $postTypes = [];
167 + function populateTermsWithPostIds( $setId, $post_type )
168 + {
169 + // Does nothing. It was already done before.
170 + }
170 171
171 - foreach ( $this->postTypes as $postType ){
172 - $pieces[] = $wpdb->prepare( "%s", $postType );
172 + /**
173 + * @param array $alreadyFilteredPosts
174 + * @return array
175 + */
176 + public function selectTerms( $alreadyFilteredPosts = [] ) {
177 + global $wpdb;
178 +
179 + $IN = false;
180 + $key_in = '';
181 + $new_result = [];
182 + $min_and_max = [
183 + 'min' => 0,
184 + 'max' => 0
185 + ];
186 + $post_and_types = [];
187 + $post_and_meta_values = [];
188 + $translatable_post_type_exists = false;
189 +
190 + /**
191 + * Set Post types
192 + */
193 + if( ! empty( $this->postTypes ) && isset($this->postTypes[0]) && $this->postTypes[0] ){
194 + foreach ( $this->postTypes as $postType ){
195 + $key_in .= '_' . $postType;
196 + $pieces[] = $wpdb->prepare( "%s", $postType );
197 + }
198 +
199 + $IN = implode(", ", $pieces );
173 200 }
174 201
175 - $IN = implode(", ", $pieces );
202 + /**
203 + * Set transient key
204 + */
205 + $transient_key = flrt_get_terms_transient_key( 'post_meta_num_'. $this->getName() . $key_in );
176 206
177 - $compare = '<=';
178 - if( $edge === 'min' ){
179 - $compare = '>=';
180 - }
207 + $result = flrt_get_transient( $transient_key );
181 208
182 - $type = $this->isDecimal( 0, $value) ? 'DECIMAL(15,6)' : 'SIGNED';
209 + // Cache format v2: ['vals' => [post_id => float|float[]], 'types' => [post_id => post_type]].
210 + // Legacy format cached raw SQL rows — detect and rebuild those.
211 + if ( ! is_array( $result ) || ! isset( $result['vals'], $result['types'] ) ) {
212 + // Get all post meta values
213 + $sql[] = "SELECT {$wpdb->postmeta}.post_id,{$wpdb->postmeta}.meta_value,{$wpdb->posts}.post_type";
214 + $sql[] = "FROM {$wpdb->postmeta}";
215 + $sql[] = "LEFT JOIN {$wpdb->posts} ON ({$wpdb->postmeta}.post_id = {$wpdb->posts}.ID)";
183 216
184 - $sql[] = "SELECT DISTINCT {$wpdb->postmeta}.post_id,{$wpdb->posts}.post_type";
185 - $sql[] = "FROM {$wpdb->postmeta}";
186 - $sql[] = "LEFT JOIN {$wpdb->posts} ON ({$wpdb->postmeta}.post_id = {$wpdb->posts}.ID)";
187 - $sql[] = "WHERE {$wpdb->postmeta}.meta_key = %s";
188 - $sql[] = "AND CAST({$wpdb->postmeta}.meta_value AS {$type}) {$compare} %s";
189 - $sql[] = "AND {$wpdb->posts}.post_type IN( {$IN} )";
217 + /**
218 + * If post type is translatable with WPML, get post meta values only with current language
219 + */
220 + if( flrt_wpml_active() && defined( 'ICL_LANGUAGE_CODE' ) ){
190 221
191 - $sql = implode(' ', $sql);
222 + $wpml_settings = get_option( 'icl_sitepress_settings' );
192 223
193 - $e_name = wp_unslash( $this->entityName );
194 - $sql = $wpdb->prepare( $sql, $e_name, $value );
195 - $result = $wpdb->get_results( $sql, ARRAY_A );
224 + foreach ( $this->postTypes as $type ) {
225 + if( isset( $wpml_settings['custom_posts_sync_option'][$type] ) ){
226 + if( $wpml_settings['custom_posts_sync_option'][$type] === '1' ){
227 + $translatable_post_type_exists = true;
228 + break;
229 + }
230 + }
231 + }
196 232
197 - if( ! empty( $result ) ){
198 - foreach( $result as $post){
199 - $postIds[] = $post['post_id'];
200 - $postTypes[$post['post_id']] = $post['post_type'];
201 - }
202 - }
233 + if ( $translatable_post_type_exists ) {
234 + $sql[] = "LEFT JOIN {$wpdb->prefix}icl_translations AS wpml_translations";
235 + $sql[] = "ON {$wpdb->postmeta}.post_id = wpml_translations.element_id";
203 236
204 - return array( 'posts' => $postIds, 'post_types' => $postTypes);
205 - }
237 + if (!empty($this->postTypes)) {
206 238
207 - function populateTermsWithPostIds( $setId, $post_type )
208 - {
209 - $wpManager = Container::instance()->getWpManager();
210 - $queriedValues = $wpManager->getQueryVar( 'queried_values' );
239 + $sql[] = "AND wpml_translations.element_type IN(";
211 240
212 - if( $queriedValues ){
213 - foreach( $queriedValues as $slug => $filter ){
214 - if( $filter['e_name'] === $this->getName() ){
215 - $values = $filter['values'];
216 - break;
241 + foreach ($this->postTypes as $type) {
242 + $LANG_IN[] = $wpdb->prepare("CONCAT('post_', '%s')", $type);
243 + }
244 + $sql[] = implode(",", $LANG_IN);
245 +
246 + $sql[] = ")";
247 + }
217 248 }
218 249 }
250 + /**
251 + * There is NULL problem because posts with meta_value = '' are also included in the list
252 + * And condition (NULL <= 0) is true
253 + * */
254 + $sql[] = "WHERE {$wpdb->postmeta}.meta_key = %s";
255 + $sql[] = "AND {$wpdb->postmeta}.meta_value IS NOT NULL";
219 256
220 - foreach( $this->items as $index => $term ){
221 - if( isset( $values[$term->slug] ) ){
222 - $term_posts = $this->getTermPosts( $term->slug, $values[$term->slug] );
223 - $this->items[$index]->posts = $term_posts['posts']; // $this->getTermPosts( $term->slug, $values[$term->slug] );
224 - $this->items[$index]->post_types = $term_posts['post_types'];
225 - }
257 + if( $IN ){
258 + $sql[] = "AND {$wpdb->posts}.post_type IN( {$IN} )";
226 259 }
227 - }
228 - }
229 260
230 - public function selectTerms( $postsIn = [] ){
261 + if( flrt_wpml_active() && defined( 'ICL_LANGUAGE_CODE' ) && $translatable_post_type_exists ){
231 262
232 - $transient_key = flrt_get_terms_transient_key( 'post_meta_num_'. $this->getName() );
263 + $sql[] = $wpdb->prepare("AND wpml_translations.language_code = '%s'", ICL_LANGUAGE_CODE);
264 + }
233 265
234 - if ( false === ( $result = get_transient( $transient_key ) ) ) {
235 - global $wpdb;
266 + $sql = implode(' ', $sql);
236 267
237 - $IN = false;
238 - if( ! empty( $this->postTypes ) && isset($this->postTypes[0]) && $this->postTypes[0] ){
239 - foreach ( $this->postTypes as $postType ){
240 - $pieces[] = $wpdb->prepare( "%s", $postType );
268 + $e_name = wp_unslash( $this->entityName );
269 + $sql = $wpdb->prepare( $sql, $e_name );
270 +
271 + /**
272 + * Filters terms SQL-query and allows to modify it
273 + */
274 + $sql = apply_filters( 'wpc_filter_get_post_meta_num_terms_sql', $sql, $e_name );
275 +
276 + $rows = $wpdb->get_results( $sql, ARRAY_A );
277 +
278 + // Aggregate raw rows into the compact v2 maps. A post can have
279 + // several meta rows with different values (e.g. WC variable
280 + // products keep one _price row per distinct child price) — such
281 + // posts get an array of floats instead of a single float.
282 + $vals = [];
283 + $types = [];
284 + foreach ( $rows as $single_post ) {
285 + if ( preg_match( '/[^\d\.\-]+/', $single_post['meta_value'] ) ) {
286 + continue;
241 287 }
242 - $IN = implode(", ", $pieces );
288 +
289 + $post_id = (int) $single_post['post_id'];
290 + $meta_value = (float) $single_post['meta_value'];
291 +
292 + if ( isset( $vals[ $post_id ] ) ) {
293 + if ( ! is_array( $vals[ $post_id ] ) ) {
294 + $vals[ $post_id ] = array( $vals[ $post_id ] );
295 + }
296 + $vals[ $post_id ][] = $meta_value;
297 + } else {
298 + $vals[ $post_id ] = $meta_value;
299 + }
300 +
301 + $types[ $post_id ] = $single_post['post_type'];
243 302 }
303 + unset( $rows );
244 304
245 - $sql[] = "SELECT {$wpdb->postmeta}.post_id,{$wpdb->postmeta}.meta_value";
246 - $sql[] = "FROM {$wpdb->postmeta}";
247 - $sql[] = "LEFT JOIN {$wpdb->posts} ON ({$wpdb->postmeta}.post_id = {$wpdb->posts}.ID)";
305 + $result = array( 'vals' => $vals, 'types' => $types );
306 + unset( $vals, $types );
248 307
249 - if( flrt_wpml_active() && defined( 'ICL_LANGUAGE_CODE' ) ){
250 - $sql[] = "LEFT JOIN {$wpdb->prefix}icl_translations AS wpml_translations";
251 - $sql[] = "ON {$wpdb->postmeta}.post_id = wpml_translations.element_id";
308 + flrt_set_transient( $transient_key, $result, FLRT_TRANSIENT_PERIOD_HOURS * HOUR_IN_SECONDS );
309 + }
252 310
253 - if( ! empty( $this->postTypes ) ){
311 + $meta_vals = isset( $result['vals'] ) ? $result['vals'] : [];
312 + $meta_types = isset( $result['types'] ) ? $result['types'] : [];
254 313
255 - $sql[] = "AND wpml_translations.element_type IN(";
314 + if( ! empty( $meta_vals ) ) {
256 315
257 - foreach( $this->postTypes as $type ){
258 - $LANG_IN[] = $wpdb->prepare( "CONCAT('post_', '%s')", $type );
259 - }
260 - $sql[] = implode(",", $LANG_IN );
316 + $postsIn_flipped = array_flip( $alreadyFilteredPosts );
317 + $wpManager = Container::instance()->getWpManager();
318 + $queried_values = $wpManager->getQueryVar( 'queried_values', [] );
319 + $fss = Container::instance()->getFilterSetService();
320 + $filter_slug = false;
321 + $setId = false;
261 322
262 - $sql[] = ")";
263 -
323 + /**
324 + * Check if this filter was queried
325 + */
326 + foreach ( $queried_values as $slug => $filter ) {
327 + if ( $filter[ 'e_name' ] === $this->getName() ) {
328 + $filter_slug = $slug;
329 + $setId = $filter['parent'];
330 + break;
264 331 }
265 332 }
266 333
267 - $sql[] = "WHERE {$wpdb->postmeta}.meta_key = %s";
334 + $max = false;
335 + $min = false;
268 336
269 - if( $IN ){
270 - $sql[] = "AND {$wpdb->posts}.post_type IN( {$IN} )";
337 + /**
338 + * If this filter was queried we have to receive its $max and $min values
339 + */
340 + if ( $filter_slug ) {
341 + if ( isset( $queried_values[ $filter_slug ][ 'values' ][ 'max' ] ) ) {
342 + $max = (float) $queried_values[ $filter_slug ][ 'values' ][ 'max' ];
343 + $max = apply_filters( 'wpc_unset_num_shift', $max, $this->getName() );
344 + }
345 +
346 + if ( isset( $queried_values[ $filter_slug ][ 'values' ][ 'min' ] ) ) {
347 + $min = (float) $queried_values[ $filter_slug ][ 'values' ][ 'min' ];
348 + $min = apply_filters( 'wpc_unset_num_shift', $min, $this->getName() );
349 + }
271 350 }
272 351
273 - if( flrt_wpml_active() && defined( 'ICL_LANGUAGE_CODE' ) ){
274 - $sql[] = $wpdb->prepare("AND wpml_translations.language_code = '%s'", ICL_LANGUAGE_CODE);
352 + if($this->is_woo_discount_rules && (strpos($this->entityName, '_price') !== false)){
353 + $wdr_woo_discount_rules = $this->getWooDiscountRulesClass();
354 + $this->preloadProducts( array_keys( $meta_vals ) );
275 355 }
276 356
277 - $sql = implode(' ', $sql);
357 + foreach ( $meta_vals as $post_id => $post_values ) {
358 + /**
359 + * If there are already filtered posts, we have to skip posts
360 + * that are out of the queried list
361 + */
362 + if( ! empty( $alreadyFilteredPosts ) ) {
363 + if( ! isset( $postsIn_flipped[ $post_id ] ) ) {
364 + continue;
365 + }
366 + }
278 367
279 - $e_name = wp_unslash( $this->entityName );
280 - $sql = $wpdb->prepare( $sql, $e_name );
368 + if ($this->is_woo_discount_rules) {
369 + if (strpos($this->entityName, '_price') !== false) {
370 + $product = $this->getProductCached($post_id);
371 + if ($product) {
372 + $wdr_product_has_sale = $wdr_woo_discount_rules->getProductPriceToDisplay($product);
373 + if ($wdr_product_has_sale) {
374 + $post_values = (float) $wdr_product_has_sale['discounted_price'];
375 + }
376 + }
377 + }
378 + }
281 379
282 - $result = $wpdb->get_results( $sql, ARRAY_A );
380 + /**
381 + * We have to generate and fill two arrays
382 + * First to detect $min and $max values
383 + * Second to map post_types with post IDs
384 + */
385 + foreach ( (array) $post_values as $meta_value ) {
386 + $post_and_meta_values[ $post_id ][] = $meta_value;
387 + $new_result[] = $meta_value;
283 388
284 - set_transient( $transient_key, $result, FLRT_TRANSIENT_PERIOD_HOURS * HOUR_IN_SECONDS );
285 - }
389 + if ( $min !== false && $meta_value < $min ){
390 + continue;
391 + }
286 392
287 - $new_result = [];
288 - $min_and_max = [
289 - 'min' => 0,
290 - 'max' => 0
291 - ];
393 + if ( $max !== false && $meta_value > $max ){
394 + continue;
395 + }
292 396
293 - if( ! empty( $result ) ){
294 - if( ! empty( $postsIn ) ){
295 - foreach ( $result as $single_post ){
296 - if( in_array( $single_post['post_id'], $postsIn ) ){
297 - $new_result[ $single_post['post_id'] ] = $single_post['meta_value'];
397 + if($this->is_woo_discount_rules){
398 + $this->wdr_product_ids[] = $post_id;
298 399 }
400 +
401 + $post_and_types[ $post_id ] = $meta_types[ $post_id ];
299 402 }
300 - }else{
301 - foreach ( $result as $single_post ){
302 - $new_result[ $single_post['post_id'] ] = $single_post['meta_value'];
303 - }
304 403 }
404 +
305 405 }
306 -
406 + $abs_min_and_max = [
407 + 'abs_min' => 0,
408 + 'abs_max' => 0,
409 + ];
307 410 if( ! empty( $new_result ) ){
308 411 $min_and_max = [
309 - 'min' => floor( min( $new_result ) ),
310 - 'max' => floor( max( $new_result ) )
412 + 'min' => apply_filters( 'wpc_set_num_shift', min( $new_result ), $this->getName(), 'min' ),
413 + 'max' => apply_filters( 'wpc_set_num_shift', max( $new_result ), $this->getName(), 'max' ),
311 414 ];
415 +
416 + $flat = [];
417 + foreach ($post_and_meta_values as $post_id => $values) {
418 + foreach ($values as $value) {
419 + $flat[$post_id][] = $value;
420 + }
421 + }
422 +
423 + $all_values = array_merge(...array_values($post_and_meta_values));
424 +
425 + $abs_min = min($all_values);
426 + $abs_max = max($all_values);
427 +
428 + $min_post_id = null;
429 + $max_post_id = null;
430 +
431 + foreach ($post_and_meta_values as $post_id => $values) {
432 + if (in_array($abs_min, $values)) {
433 + $min_post_id = $post_id;
434 + }
435 + if (in_array($abs_max, $values)) {
436 + $max_post_id = $post_id;
437 + }
438 + }
439 +
440 + $abs_min_and_max = [
441 + 'abs_min' => $abs_min,
442 + 'abs_max' => $abs_max,
443 + ];
444 +
445 + $post_and_meta_values[$min_post_id] = [$min_and_max['min']];
446 + $post_and_meta_values[$max_post_id] = [$min_and_max['max']];
312 447 }
313 448
314 - return $this->convertSelectResult( $min_and_max );
449 + $min_and_max = apply_filters( 'wpc_set_min_max', $min_and_max, $this->getName() );
450 + return $this->convertSelectResult( $min_and_max, $post_and_types, $post_and_meta_values, $abs_min_and_max);
315 451 }
316 452
317 453 public function updateMinAndMaxValues( $postsIn )
318 454 {
@@ -338,11 +474,16 @@
338 474 break;
339 475 }
340 476 }
341 477
342 - $name = $name .' '. $slug;
478 + if ( $queriedFilter ) {
479 + $name = $name .' '. $slug;
480 + }
343 481 }
344 482
483 + $name = ($name === 'min') ? esc_html__('min', 'filter-everything') : $name;
484 + $name = ($name === 'max') ? esc_html__('max', 'filter-everything') : $name;
485 +
345 486 if( isset( $queriedFilter['values'][$edge] ) ) {
346 487 $name = $name .' '. $queriedFilter['values'][$edge];
347 488 }else{
348 489 $name = $name .' '. $value;
@@ -350,9 +491,9 @@
350 491
351 492 return apply_filters( 'wpc_filter_post_meta_num_term_name', $name, $this->getName() );
352 493 }
353 494
354 - private function convertSelectResult( $result ){
495 + public function convertSelectResult( $result, $post_and_types = [], $post_and_meta_values = [], $abs_min_and_max = [] ){
355 496 $return = [];
356 497
357 498 if( ! is_array( $result ) ){
358 499 return $return;
@@ -368,13 +509,16 @@
368 509 $termObject = new \stdClass();
369 510 $termObject->slug = $edge;
370 511 $termObject->name = $this->createTermName( $edge, $value, $queried_values );
371 512 $termObject->term_id = $edge . '_' . $this->getName();
372 - $termObject->posts = [];
513 + $termObject->posts = array_keys( $post_and_types );
514 + $termObject->meta_values = $post_and_meta_values;
373 515 $termObject->count = 0;
374 516 $termObject->cross_count = 0;
375 - $termObject->post_types = [];
517 + $termObject->post_types = $post_and_types; //[];
376 518 $termObject->$edge = $value;
519 + $termObject->wp_queried = false;
520 + $termObject->abs_values = $abs_min_and_max;
377 521
378 522 $return[ $edge ] = $termObject;
379 523
380 524 $i++;
@@ -382,254 +526,144 @@
382 526
383 527 return $return;
384 528 }
385 529
386 - private function isTermInMetaKey( $queried_value, $wp_query ){
387 - $duplicate = [];
388 - $terms = $queried_value['values'];
389 - $meta_key = $wp_query->get('meta_key');
390 - $meta_value = $wp_query->get('meta_value');
530 + /**
531 + * @return object WP_Query
532 + */
533 + public function addTermsToWpQuery( $queried_value, $wp_query )
534 + {
535 + $meta_query = [];
536 + $key = $queried_value['e_name'];
537 + // Add existing Meta Query if present
538 + $this->importExistingMetaQuery( $wp_query );
391 539
392 - foreach ( $terms as $term ) {
393 - if( $queried_value['e_name'] === $meta_key ){
394 - if( $meta_value === $term ){
395 - $duplicate['post_meta'] = $queried_value['e_name'];
396 - $duplicate['term'] = $term;
397 - return $duplicate;
398 - }
399 - }
400 - }
540 + /**
541 + * @bug for Woo Products if we don't specify Max value it makes it 0.0000
542 + */
543 + $min = isset( $queried_value['values']['min'] ) ? $queried_value['values']['min'] : false;
544 + $max = isset( $queried_value['values']['max'] ) ? $queried_value['values']['max'] : false;
401 545
402 - return false;
403 - }
404 -
405 - private function isTermInMetaQuery( $queried_value, $wp_query ){
406 - $duplicate = [];
407 - $meta_query = $wp_query->get('meta_query');
408 - $terms = $queried_value['values'];
409 -
410 - if( ! empty( $meta_query ) ){
411 -
412 - foreach ( $meta_query as $query_array ){
413 - if( isset( $query_array['key'] ) && $query_array['key'] === $queried_value['e_name'] ){
414 - if( isset( $query_array['value'] ) && in_array( $query_array['value'], $terms ) ){
415 - $duplicate['post_meta'] = $queried_value['e_name'];
416 - $duplicate['term'] = $query_array['value'];
417 - return $duplicate;
418 - }
419 - }
546 + // Compare with false because $min can be 0
547 + if ((strpos($key, '_price') !== false) && flrt_is_woocommerce() && $this->is_woo_discount_rules) {
548 + $query_post_in = $this->wdrAddProductsRangeToFilterQuery($wp_query);
549 + $wp_query->set('post__in', $query_post_in);
550 + if (empty($query_post_in)){
551 + $wp_query->set('posts_per_page', $query_post_in);
420 552 }
421 - }
553 + } else {
554 + if ($min !== false && $max !== false) {
555 + $min = apply_filters('wpc_unset_num_shift', $min, $this->getName());
556 + $max = apply_filters('wpc_unset_num_shift', $max, $this->getName());
422 557
423 - return false;
424 - }
558 + $type = ( $this->isDecimal($queried_value['step'], $min) || $this->isDecimal($queried_value['step'], $max) ) ? 'DECIMAL(15,6)' : 'NUMERIC';
559 + /**
560 + * Both bounds must live in ONE clause. WP_Meta_Query gives every
561 + * clause its own postmeta JOIN, so separate >= and <= clauses can
562 + * match two DIFFERENT meta rows: a post with several values of the
563 + * key (e.g. variable product _price rows) passed the range even
564 + * when no single value was inside it, and the results disagreed
565 + * with the term counters. BETWEEN checks the same row.
566 + */
567 + $meta_query = array(
568 + 'key' => $key,
569 + 'value' => array( $min, $max ),
570 + 'compare' => 'BETWEEN',
571 + 'type' => $type
572 + );
573 + $this->addMetaQueryArray($meta_query);
425 574
426 - public function isTermAlreadyInQuery( $queried_value, $wp_query )
427 - {
428 - // Is term in Key
429 - if( $duplicate = $this->isTermInMetaKey( $queried_value, $wp_query ) ){
430 - return $duplicate;
431 - }
432 - // Is term in Query
433 - if( $duplicate = $this->isTermInMetaQuery( $queried_value, $wp_query ) ){
434 - return $duplicate;
435 - }
575 + } elseif ($min !== false) {
576 + $min = apply_filters('wpc_unset_num_shift', $min, $this->getName());
436 577
437 - return false;
438 - }
578 + $type = $this->isDecimal($queried_value['step'], $min) ? 'DECIMAL(15,6)' : 'NUMERIC';
579 + $meta_query = array(
580 + 'key' => $key,
581 + 'value' => $min,
582 + 'compare' => '>=',
583 + 'type' => $type
584 + );
585 + $this->addMetaQueryArray($meta_query);
439 586
440 - private function normalizeMetaQueryArray( $meta_query )
441 - {
442 - $normalized_meta_query = [];
587 + } elseif ($max !== false) {
588 + $max = apply_filters('wpc_unset_num_shift', $max, $this->getName());
443 589
444 - if( ! is_array( $meta_query ) || ! isset( $meta_query['key'] ) ){
445 - return false;
446 - }
447 - if( isset( $meta_query['value'] ) ){
448 - if( is_array( $meta_query['value'] ) ){
449 - sort( $meta_query['value'] );
450 - $meta_query['value'] = implode( '-', $meta_query['value'] );
451 - $normalized_meta_query['value'] = $meta_query['value'];
452 - }else{
453 - $normalized_meta_query['value'] = $meta_query['value'];
590 + $type = $this->isDecimal($queried_value['step'], $max) ? 'DECIMAL(15,6)' : 'NUMERIC';
591 + $meta_query = array(
592 + 'key' => $key,
593 + 'value' => $max,
594 + 'compare' => '<=',
595 + 'type' => $type
596 + );
597 + $this->addMetaQueryArray($meta_query);
454 598 }
455 - }
456 599
457 - $normalized_meta_query['key'] = $meta_query['key'];
458 - if( isset( $meta_query['compare'] ) ){
459 - $normalized_meta_query['compare'] = isset( $meta_query['compare'] ) ? $meta_query['compare'] : '';
600 + if (count($this->new_meta_query) > 1) {
601 + $this->new_meta_query['relation'] = 'AND';
602 + }
460 603 }
461 604
462 - return $normalized_meta_query;
463 - }
605 + $wp_query->set('meta_query', $this->new_meta_query);
464 606
465 - private function isTheSameMetaQuery( $meta_query_1, $meta_query_2 )
466 - {
467 - $meta_query_1 = $this->normalizeMetaQueryArray($meta_query_1);
468 - $meta_query_2 = $this->normalizeMetaQueryArray($meta_query_2);
607 + $this->new_meta_query = [];
469 608
470 - $diff = array_diff( $meta_query_1, $meta_query_2 );
471 -
472 - if ( empty( $diff ) ){
473 - return true;
474 - }
475 -
476 - return false;
609 + return $wp_query;
477 610 }
478 611
479 - public function addMetaQueryArray( $meta_query_array, $relation = false )
612 + public function wdrAddProductsRangeToFilterQuery($wp_query)
480 613 {
481 - if( ! isset( $meta_query_array['key'] ) ){
482 - return false;
483 - }
614 + $args = array(
615 + 'status' => 'publish',
616 + 'limit' => -1,
617 + 'return' => 'ids'
618 + );
484 619
485 - $existing_meta_query = $this->new_meta_query;
620 + //Added for work woo_discount_rules and query hash
621 + $args['flrt_wdr_pagination'] = true;
486 622
487 - foreach ($existing_meta_query as $index => $present_query) {
488 -
489 - if ($this->hasNestedQueries($present_query)) {
490 - foreach ($present_query as $k => $nested_present_query) {
491 -
492 - if (!isset($nested_present_query['key'])) {
493 - // relation arg
494 - continue;
495 - }
496 - if ($this->isTheSameMetaQuery($nested_present_query, $meta_query_array)) {
497 - return false;
498 - }
499 - }
500 - } else {
501 - if ($this->isTheSameMetaQuery($present_query, $meta_query_array)) {
502 - return false;
503 - }
504 - }
505 -
623 + if (isset($wp_query->queried_object->taxonomy) && isset($wp_query->queried_object->taxonomy)) {
624 + $args['tax_query'] = array(
625 + array(
626 + 'taxonomy' => $wp_query->queried_object->taxonomy,
627 + 'field' => 'term_id',
628 + 'terms' => array($wp_query->queried_object->term_id),
629 + )
630 + );
506 631 }
507 632
508 - if( $relation && in_array( $relation, array( 'AND', 'OR' ) ) ){
509 - $nested_index = $this->findNestedIndexForQuery($meta_query_array);
510 - $this->new_meta_query[$nested_index][] = $meta_query_array;
511 - $this->new_meta_query[$nested_index]['relation'] = $relation;
512 - }else{
513 - $this->new_meta_query[] = $meta_query_array;
633 + $products = wc_get_products($args);
634 + $query_post_in = array_intersect($products, $this->wdr_product_ids);
635 + if(isset($wp_query->query_vars['post__in']) && !empty($wp_query->query_vars['post__in'])){
636 + $query_post_in = array_intersect($query_post_in, $wp_query->query_vars['post__in']);
514 637 }
515 -
516 - }
517 -
518 - public function findNestedIndexForQuery( $meta_query_array )
519 - {
520 - $meta_key = $meta_query_array['key'];
521 -
522 - if( empty( $this->new_meta_query ) ){
523 - return 0;
638 + if(empty($query_post_in)){
639 + return [0];
524 640 }
525 -
526 - foreach ( $this->new_meta_query as $i_level_1 => $maybe_meta_query ){
527 - // This subquery already exists
528 - if( isset( $maybe_meta_query[0]['key'] ) && $maybe_meta_query[0]['key'] === $meta_key ){
529 - return $i_level_1;
530 - }
531 - }
532 -
533 - return count( $this->new_meta_query );
641 + return $query_post_in;
534 642 }
535 643
536 - public function hasNestedQueries( $meta_query )
537 - {
538 - if( isset( $meta_query[0]['key'] ) ){
539 - return true;
540 - }
541 -
542 - return false;
644 + public function getWooDiscountRulesClass(){
645 + return flrt_woo_discount_rules_class();
543 646 }
544 647
545 - public function addMetaKeyToQuery( $wp_query )
546 - {
547 - $args = [];
548 -
549 - $args['key'] = $wp_query->get( 'meta_key' );
550 - if( $wp_query->get( 'meta_value' ) ){
551 - $args['value'] = $wp_query->get( 'meta_value' );
552 - $args['compare'] = ( $compare = $wp_query->get( 'meta_compare' ) ) ? $compare : 'IN';
648 + protected function preloadProducts($product_ids) {
649 + if (empty($product_ids)) {
650 + return;
553 651 }
554 652
555 - $wp_query->set( 'meta_key', '' );
556 - $wp_query->set( 'meta_value', '' );
653 + $products = wc_get_products([
654 + 'include' => $product_ids,
655 + 'limit' => -1,
656 + ]);
557 657
558 - $this->addMetaQueryArray( $args );
559 - }
560 -
561 - public function importExistingMetaQuery( $wp_query )
562 - {
563 - // Try to check if there is meta_key, meta_value and meta_compare
564 - if( $wp_query->get('meta_key') ){
565 - $this->addMetaKeyToQuery( $wp_query );
658 + foreach ($products as $product) {
659 + $this->product_cache[$product->get_id()] = $product;
566 660 }
567 -
568 - $already_existing_meta_query = $wp_query->get('meta_query');
569 -
570 - if( is_array( $already_existing_meta_query ) ){
571 - foreach( $already_existing_meta_query as $value ){
572 - if( $this->hasNestedQueries( $value ) ){
573 - foreach( $value as $n => $nested_meta_query ){
574 - $this->addMetaQueryArray( $nested_meta_query, $value['relation'] );
575 - }
576 - }else{
577 - $this->addMetaQueryArray( $value );
578 - }
579 -
580 - }
581 - }
582 661 }
583 662
584 - /**
585 - * @return object WP_Query
586 - */
587 - public function addTermsToWpQuery( $queried_value, $wp_query )
588 - {
589 - $meta_query = [];
590 - $key = $queried_value['e_name'];
591 - // Add existing Meta Query if present
592 - $this->importExistingMetaQuery($wp_query);
593 -
594 - /**
595 - * @bug for Woo Products if we don't specify Max value it makes it 0.0000
596 - */
597 - $min = isset( $queried_value['values']['min'] ) ? $queried_value['values']['min'] : false;
598 - $max = isset( $queried_value['values']['max'] ) ? $queried_value['values']['max'] : false;
599 -
600 - // Compare with false because $min can be 0
601 - if( $min !== false ){
602 - $type = $this->isDecimal( $queried_value['step'], $min ) ? 'DECIMAL(15,6)' : 'NUMERIC';
603 - $meta_query = array(
604 - 'key' => $key,
605 - 'value' => $min,
606 - 'compare' => '>=',
607 - 'type' => $type
608 - );
609 - $this->addMetaQueryArray( $meta_query );
663 + protected function getProductCached($product_id) {
664 + if (!isset($this->product_cache[$product_id])) {
665 + $this->product_cache[$product_id] = wc_get_product($product_id);
610 666 }
611 -
612 - if( $max !== false ){
613 - $type = $this->isDecimal( $queried_value['step'], $max ) ? 'DECIMAL(15,6)' : 'NUMERIC';
614 - $meta_query = array(
615 - 'key' => $key,
616 - 'value' => $max,
617 - 'compare' => '<=',
618 - 'type' => $type
619 - );
620 - $this->addMetaQueryArray( $meta_query );
621 - }
622 -
623 - $this->addMetaQueryArray( $meta_query );
624 -
625 -
626 - if( count($this->new_meta_query) > 1 ){
627 - $this->new_meta_query['relation'] = 'AND';
628 - }
629 -
630 - $wp_query->set('meta_query', $this->new_meta_query );
631 - $this->new_meta_query = [];
632 -
633 - return $wp_query;
667 + return $this->product_cache[$product_id];
634 668 }
635 669 }