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 -345 1.5.11.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 }
@@ -161,158 +163,292 @@
161 163
162 164 return false;
163 165 }
164 166
165 - private function getTermPosts( $edge, $value )
166 - { global $wpdb;
167 - $postIds = [];
168 - $postTypes = [];
167 + function populateTermsWithPostIds( $setId, $post_type )
168 + {
169 + // Does nothing. It was already done before.
170 + }
169 171
170 - foreach ( $this->postTypes as $postType ){
171 - $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 );
172 200 }
173 201
174 - $IN = implode(", ", $pieces );
202 + /**
203 + * Set transient key
204 + */
205 + $transient_key = flrt_get_terms_transient_key( 'post_meta_num_'. $this->getName() . $key_in );
175 206
176 - $compare = '<=';
177 - if( $edge === 'min' ){
178 - $compare = '>=';
179 - }
207 + $result = flrt_get_transient( $transient_key );
180 208
181 - $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)";
182 216
183 - $sql[] = "SELECT DISTINCT {$wpdb->postmeta}.post_id,{$wpdb->posts}.post_type";
184 - $sql[] = "FROM {$wpdb->postmeta}";
185 - $sql[] = "LEFT JOIN {$wpdb->posts} ON ({$wpdb->postmeta}.post_id = {$wpdb->posts}.ID)";
186 - $sql[] = "WHERE {$wpdb->postmeta}.meta_key = %s";
187 - $sql[] = "AND CAST({$wpdb->postmeta}.meta_value AS {$type}) {$compare} %s";
188 - $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' ) ){
189 221
190 - $sql = implode(' ', $sql);
222 + $wpml_settings = get_option( 'icl_sitepress_settings' );
191 223
192 - $e_name = wp_unslash( $this->entityName );
193 - $sql = $wpdb->prepare( $sql, $e_name, $value );
194 - $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 + }
195 232
196 - if( ! empty( $result ) ){
197 - foreach( $result as $post){
198 - $postIds[] = $post['post_id'];
199 - $postTypes[$post['post_id']] = $post['post_type'];
200 - }
201 - }
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";
202 236
203 - return array( 'posts' => $postIds, 'post_types' => $postTypes);
204 - }
237 + if (!empty($this->postTypes)) {
205 238
206 - function populateTermsWithPostIds( $setId, $post_type )
207 - {
208 - $wpManager = Container::instance()->getWpManager();
209 - $queriedValues = $wpManager->getQueryVar( 'queried_values' );
239 + $sql[] = "AND wpml_translations.element_type IN(";
210 240
211 - if( $queriedValues ){
212 - foreach( $queriedValues as $slug => $filter ){
213 - if( $filter['e_name'] === $this->getName() ){
214 - $values = $filter['values'];
215 - 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 + }
216 248 }
217 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";
218 256
219 - foreach( $this->items as $index => $term ){
220 - if( isset( $values[$term->slug] ) ){
221 - $term_posts = $this->getTermPosts( $term->slug, $values[$term->slug] );
222 - $this->items[$index]->posts = $term_posts['posts']; // $this->getTermPosts( $term->slug, $values[$term->slug] );
223 - $this->items[$index]->post_types = $term_posts['post_types'];
224 - }
257 + if( $IN ){
258 + $sql[] = "AND {$wpdb->posts}.post_type IN( {$IN} )";
225 259 }
226 - }
227 - }
228 260
229 - public function selectTerms( $postsIn = [] ){
261 + if( flrt_wpml_active() && defined( 'ICL_LANGUAGE_CODE' ) && $translatable_post_type_exists ){
230 262
231 - $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 + }
232 265
233 - if ( false === ( $result = get_transient( $transient_key ) ) ) {
234 - global $wpdb;
266 + $sql = implode(' ', $sql);
235 267
236 - $IN = false;
237 - if( ! empty( $this->postTypes ) && isset($this->postTypes[0]) && $this->postTypes[0] ){
238 - foreach ( $this->postTypes as $postType ){
239 - $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;
240 287 }
241 - $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'];
242 302 }
303 + unset( $rows );
243 304
244 - $sql[] = "SELECT {$wpdb->postmeta}.post_id,{$wpdb->postmeta}.meta_value";
245 - $sql[] = "FROM {$wpdb->postmeta}";
246 - $sql[] = "LEFT JOIN {$wpdb->posts} ON ({$wpdb->postmeta}.post_id = {$wpdb->posts}.ID)";
305 + $result = array( 'vals' => $vals, 'types' => $types );
306 + unset( $vals, $types );
247 307
248 - if( flrt_wpml_active() && defined( 'ICL_LANGUAGE_CODE' ) ){
249 - $sql[] = "LEFT JOIN {$wpdb->prefix}icl_translations AS wpml_translations";
250 - $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 + }
251 310
252 - if( ! empty( $this->postTypes ) ){
311 + $meta_vals = isset( $result['vals'] ) ? $result['vals'] : [];
312 + $meta_types = isset( $result['types'] ) ? $result['types'] : [];
253 313
254 - $sql[] = "AND wpml_translations.element_type IN(";
314 + if( ! empty( $meta_vals ) ) {
255 315
256 - foreach( $this->postTypes as $type ){
257 - $LANG_IN[] = $wpdb->prepare( "CONCAT('post_', '%s')", $type );
258 - }
259 - $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;
260 322
261 - $sql[] = ")";
262 -
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;
263 331 }
264 332 }
265 333
266 - $sql[] = "WHERE {$wpdb->postmeta}.meta_key = %s";
334 + $max = false;
335 + $min = false;
267 336
268 - if( $IN ){
269 - $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 + }
270 350 }
271 351
272 - if( flrt_wpml_active() && defined( 'ICL_LANGUAGE_CODE' ) ){
273 - $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 ) );
274 355 }
275 356
276 - $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 + }
277 367
278 - $e_name = wp_unslash( $this->entityName );
279 - $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 + }
280 379
281 - $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;
282 388
283 - set_transient( $transient_key, $result, FLRT_TRANSIENT_PERIOD_HOURS * HOUR_IN_SECONDS );
284 - }
389 + if ( $min !== false && $meta_value < $min ){
390 + continue;
391 + }
285 392
286 - $new_result = [];
287 - $min_and_max = [
288 - 'min' => 0,
289 - 'max' => 0
290 - ];
393 + if ( $max !== false && $meta_value > $max ){
394 + continue;
395 + }
291 396
292 - if( ! empty( $result ) ){
293 - if( ! empty( $postsIn ) ){
294 - $postsIn_flipped = array_flip($postsIn);
295 - foreach ( $result as $single_post ){
296 - if( isset( $postsIn_flipped[ $single_post['post_id'] ] ) ){
297 - $new_result[ $single_post['post_id'] ] = (int) $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'] ] = (int) $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,255 +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 - $terms_flipped = array_flip($terms);
415 - if( isset( $query_array['value'] ) && isset( $terms_flipped[ $query_array['value'] ] ) ){
416 - $duplicate['post_meta'] = $queried_value['e_name'];
417 - $duplicate['term'] = $query_array['value'];
418 - return $duplicate;
419 - }
420 - }
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);
421 552 }
422 - }
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());
423 557
424 - return false;
425 - }
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);
426 574
427 - public function isTermAlreadyInQuery( $queried_value, $wp_query )
428 - {
429 - // Is term in Key
430 - if( $duplicate = $this->isTermInMetaKey( $queried_value, $wp_query ) ){
431 - return $duplicate;
432 - }
433 - // Is term in Query
434 - if( $duplicate = $this->isTermInMetaQuery( $queried_value, $wp_query ) ){
435 - return $duplicate;
436 - }
575 + } elseif ($min !== false) {
576 + $min = apply_filters('wpc_unset_num_shift', $min, $this->getName());
437 577
438 - return false;
439 - }
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);
440 586
441 - private function normalizeMetaQueryArray( $meta_query )
442 - {
443 - $normalized_meta_query = [];
587 + } elseif ($max !== false) {
588 + $max = apply_filters('wpc_unset_num_shift', $max, $this->getName());
444 589
445 - if( ! is_array( $meta_query ) || ! isset( $meta_query['key'] ) ){
446 - return false;
447 - }
448 - if( isset( $meta_query['value'] ) ){
449 - if( is_array( $meta_query['value'] ) ){
450 - sort( $meta_query['value'] );
451 - $meta_query['value'] = implode( '-', $meta_query['value'] );
452 - $normalized_meta_query['value'] = $meta_query['value'];
453 - }else{
454 - $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);
455 598 }
456 - }
457 599
458 - $normalized_meta_query['key'] = $meta_query['key'];
459 - if( isset( $meta_query['compare'] ) ){
460 - $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 + }
461 603 }
462 604
463 - return $normalized_meta_query;
464 - }
605 + $wp_query->set('meta_query', $this->new_meta_query);
465 606
466 - private function isTheSameMetaQuery( $meta_query_1, $meta_query_2 )
467 - {
468 - $meta_query_1 = $this->normalizeMetaQueryArray($meta_query_1);
469 - $meta_query_2 = $this->normalizeMetaQueryArray($meta_query_2);
607 + $this->new_meta_query = [];
470 608
471 - $diff = array_diff( $meta_query_1, $meta_query_2 );
472 -
473 - if ( empty( $diff ) ){
474 - return true;
475 - }
476 -
477 - return false;
609 + return $wp_query;
478 610 }
479 611
480 - public function addMetaQueryArray( $meta_query_array, $relation = false )
612 + public function wdrAddProductsRangeToFilterQuery($wp_query)
481 613 {
482 - if( ! isset( $meta_query_array['key'] ) ){
483 - return false;
484 - }
614 + $args = array(
615 + 'status' => 'publish',
616 + 'limit' => -1,
617 + 'return' => 'ids'
618 + );
485 619
486 - $existing_meta_query = $this->new_meta_query;
620 + //Added for work woo_discount_rules and query hash
621 + $args['flrt_wdr_pagination'] = true;
487 622
488 - foreach ($existing_meta_query as $index => $present_query) {
489 -
490 - if ($this->hasNestedQueries($present_query)) {
491 - foreach ($present_query as $k => $nested_present_query) {
492 -
493 - if (!isset($nested_present_query['key'])) {
494 - // relation arg
495 - continue;
496 - }
497 - if ($this->isTheSameMetaQuery($nested_present_query, $meta_query_array)) {
498 - return false;
499 - }
500 - }
501 - } else {
502 - if ($this->isTheSameMetaQuery($present_query, $meta_query_array)) {
503 - return false;
504 - }
505 - }
506 -
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 + );
507 631 }
508 632
509 - if( $relation && in_array( $relation, array( 'AND', 'OR' ) ) ){
510 - $nested_index = $this->findNestedIndexForQuery($meta_query_array);
511 - $this->new_meta_query[$nested_index][] = $meta_query_array;
512 - $this->new_meta_query[$nested_index]['relation'] = $relation;
513 - }else{
514 - $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']);
515 637 }
516 -
517 - }
518 -
519 - public function findNestedIndexForQuery( $meta_query_array )
520 - {
521 - $meta_key = $meta_query_array['key'];
522 -
523 - if( empty( $this->new_meta_query ) ){
524 - return 0;
638 + if(empty($query_post_in)){
639 + return [0];
525 640 }
526 -
527 - foreach ( $this->new_meta_query as $i_level_1 => $maybe_meta_query ){
528 - // This subquery already exists
529 - if( isset( $maybe_meta_query[0]['key'] ) && $maybe_meta_query[0]['key'] === $meta_key ){
530 - return $i_level_1;
531 - }
532 - }
533 -
534 - return count( $this->new_meta_query );
641 + return $query_post_in;
535 642 }
536 643
537 - public function hasNestedQueries( $meta_query )
538 - {
539 - if( isset( $meta_query[0]['key'] ) ){
540 - return true;
541 - }
542 -
543 - return false;
644 + public function getWooDiscountRulesClass(){
645 + return flrt_woo_discount_rules_class();
544 646 }
545 647
546 - public function addMetaKeyToQuery( $wp_query )
547 - {
548 - $args = [];
549 -
550 - $args['key'] = $wp_query->get( 'meta_key' );
551 - if( $wp_query->get( 'meta_value' ) ){
552 - $args['value'] = $wp_query->get( 'meta_value' );
553 - $args['compare'] = ( $compare = $wp_query->get( 'meta_compare' ) ) ? $compare : 'IN';
648 + protected function preloadProducts($product_ids) {
649 + if (empty($product_ids)) {
650 + return;
554 651 }
555 652
556 - $wp_query->set( 'meta_key', '' );
557 - $wp_query->set( 'meta_value', '' );
653 + $products = wc_get_products([
654 + 'include' => $product_ids,
655 + 'limit' => -1,
656 + ]);
558 657
559 - $this->addMetaQueryArray( $args );
560 - }
561 -
562 - public function importExistingMetaQuery( $wp_query )
563 - {
564 - // Try to check if there is meta_key, meta_value and meta_compare
565 - if( $wp_query->get('meta_key') ){
566 - $this->addMetaKeyToQuery( $wp_query );
658 + foreach ($products as $product) {
659 + $this->product_cache[$product->get_id()] = $product;
567 660 }
568 -
569 - $already_existing_meta_query = $wp_query->get('meta_query');
570 -
571 - if( is_array( $already_existing_meta_query ) ){
572 - foreach( $already_existing_meta_query as $value ){
573 - if( $this->hasNestedQueries( $value ) ){
574 - foreach( $value as $n => $nested_meta_query ){
575 - $this->addMetaQueryArray( $nested_meta_query, $value['relation'] );
576 - }
577 - }else{
578 - $this->addMetaQueryArray( $value );
579 - }
580 -
581 - }
582 - }
583 661 }
584 662
585 - /**
586 - * @return object WP_Query
587 - */
588 - public function addTermsToWpQuery( $queried_value, $wp_query )
589 - {
590 - $meta_query = [];
591 - $key = $queried_value['e_name'];
592 - // Add existing Meta Query if present
593 - $this->importExistingMetaQuery($wp_query);
594 -
595 - /**
596 - * @bug for Woo Products if we don't specify Max value it makes it 0.0000
597 - */
598 - $min = isset( $queried_value['values']['min'] ) ? $queried_value['values']['min'] : false;
599 - $max = isset( $queried_value['values']['max'] ) ? $queried_value['values']['max'] : false;
600 -
601 - // Compare with false because $min can be 0
602 - if( $min !== false ){
603 - $type = $this->isDecimal( $queried_value['step'], $min ) ? 'DECIMAL(15,6)' : 'NUMERIC';
604 - $meta_query = array(
605 - 'key' => $key,
606 - 'value' => $min,
607 - 'compare' => '>=',
608 - 'type' => $type
609 - );
610 - $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);
611 666 }
612 -
613 - if( $max !== false ){
614 - $type = $this->isDecimal( $queried_value['step'], $max ) ? 'DECIMAL(15,6)' : 'NUMERIC';
615 - $meta_query = array(
616 - 'key' => $key,
617 - 'value' => $max,
618 - 'compare' => '<=',
619 - 'type' => $type
620 - );
621 - $this->addMetaQueryArray( $meta_query );
622 - }
623 -
624 - $this->addMetaQueryArray( $meta_query );
625 -
626 -
627 - if( count($this->new_meta_query) > 1 ){
628 - $this->new_meta_query['relation'] = 'AND';
629 - }
630 -
631 - $wp_query->set('meta_query', $this->new_meta_query );
632 - $this->new_meta_query = [];
633 -
634 - return $wp_query;
667 + return $this->product_cache[$product_id];
635 668 }
636 669 }