PluginProbe
Smart Post – Post Grid, Post Carousel, Post Slider Gutenberg Blocks for Blog & News / 4.0.3
Smart Post – Post Grid, Post Carousel, Post Slider Gutenberg Blocks for Blog & News v4.0.3
4.0.8 4.0.7 4.0.6 4.0.5 4.0.4 4.0.3 4.0.2 4.0.1 2.3.5 2.3.6 2.4.0 2.4.1 2.4.10 2.4.11 2.4.12 2.4.13 2.4.14 2.4.15 2.4.16 2.4.17 2.4.18 2.4.19 2.4.2 2.4.20 2.4.21 All 88 releases
post-carousel / blocks / includes / class-query-filter.php

class-query-filter.php in Smart Post – Post Grid, Post Carousel, Post Slider Gutenberg Blocks for Blog & News 4.0.3, at blocks/includes/class-query-filter.php

896 lines 27.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * The file that defines the query filter class.
4 *
5 * A class definition that includes attributes and functions used to filter posts.
6 *
7 * @link https://shapedplugin.com/
8 * @since 2.0.0
9 *
10 * @package Smart_Post_Show
11 * @subpackage Smart_Post_Show/blocks
12 */
13
14 namespace SmartPostShow\Blocks;
15
16 if ( ! defined( 'ABSPATH' ) ) {
17 exit;
18 }
19
20 /**
21 * Class to handle WordPress post queries with advanced filtering.
22 */
23 class PostQueryHandler {
24
25 /**
26 * Get current date components for filtering.
27 *
28 * @return array Current date components (day, month, year, week).
29 */
30 private static function get_current_date_components(): array {
31 $now = new \DateTime( 'now' );
32 return array(
33 'day' => $now->format( 'j' ),
34 'month' => $now->format( 'n' ),
35 'year' => $now->format( 'Y' ),
36 'week' => $now->format( 'W' ),
37 );
38 }
39
40 /**
41 * Parse a date string into components.
42 *
43 * @param string $date Date string.
44 * @return array Parsed date components (year, month, day).
45 */
46 private static function parse_date( string $date ): array {
47 return date_parse( gmdate( 'Y-m-d', strtotime( $date ) ) );
48 }
49
50 /**
51 * Build date query for specific date filters.
52 *
53 * @param string $filter_type Type of date filter.
54 * @param array $params Additional parameters for specific filters.
55 * @return array Date query array.
56 */
57 private static function build_date_query( string $filter_type, array $params ): array {
58 $current = self::get_current_date_components();
59 $query = array();
60 switch ( $filter_type ) {
61 case 'yesterday':
62 $yesterday = self::parse_date( 'yesterday' );
63 $query = array(
64 'year' => $yesterday['year'],
65 'month' => $yesterday['month'],
66 'day' => $yesterday['day'],
67 );
68 break;
69 case 'today_only':
70 $query = array(
71 'year' => $current['year'],
72 'month' => $current['month'],
73 'day' => $current['day'],
74 );
75 break;
76 case 'today_onwards':
77 $query = array( 'after' => gmdate( 'Y-m-d', strtotime( 'yesterday' ) ) );
78 break;
79 case 'this_week':
80 $query = array(
81 'year' => $current['year'],
82 'week' => $current['week'],
83 );
84 break;
85 case 'this_month':
86 $query = array(
87 'year' => $current['year'],
88 'month' => $current['month'],
89 );
90 break;
91 case 'this_year':
92 $query = array( 'year' => $current['year'] );
93 break;
94 case 'week_ago':
95 case 'month_ago':
96 case 'year_ago':
97 $query = array(
98 'column' => 'post_date',
99 'after' => sprintf( '1 %s ago', str_replace( '_ago', '', $filter_type ) ),
100 );
101 break;
102 case 'specific_date_before':
103 $date = self::parse_date( $params['specific_date_before'] ?? '' );
104 $query = array(
105 'before' => array(
106 'year' => $date['year'],
107 'month' => $date['month'],
108 'day' => $date['day'],
109 ),
110 'inclusive' => true,
111 );
112 break;
113 case 'specific_date_after':
114 $date = self::parse_date( $params['specific_date_after'] ?? '' );
115 $query = array(
116 'after' => array(
117 'year' => $date['year'],
118 'month' => $date['month'],
119 'day' => $date['day'],
120 ),
121 'inclusive' => true,
122 );
123 break;
124
125 case 'specific_date':
126 $date = self::parse_date( $params['specific_date'] ?? '' );
127 $query = array(
128 'year' => $date['year'],
129 'month' => $date['month'],
130 'day' => $date['day'],
131 );
132 break;
133 case 'past_quarter':
134 $query = array(
135 'after' => '3 months ago',
136 'inclusive' => false,
137 );
138 break;
139 case 'specific_month':
140 $query = array( 'month' => $params['specific_month'] ?? 1 );
141 break;
142 case 'specific_year':
143 $query = array( 'year' => $params['specific_year'] ?? 2024 );
144 break;
145
146 case 'specific_period':
147 $after = self::parse_date( $params['specific_period_after'] ?? '' );
148 $before = self::parse_date( $params['specific_period_before'] ?? '' );
149 if ( $after && $before ) {
150 $query = array(
151 'after' => array(
152 'year' => $after['year'],
153 'month' => $after['month'],
154 'day' => $after['day'],
155 ),
156 'before' => array(
157 'year' => $before['year'],
158 'month' => $before['month'],
159 'day' => $before['day'],
160 ),
161 'inclusive' => true,
162 );
163 }
164 break;
165 }
166
167 return $query;
168 }
169
170 /**
171 * Build exclude date query.
172 *
173 * @param string $exclude_date_before Date to exclude before.
174 * @param string $exclude_date_after Date to exclude after.
175 * @return array Exclude date query array.
176 */
177 private static function build_exclude_date_query( string $exclude_date_before, string $exclude_date_after ): array {
178 $query = array();
179 $after = self::parse_date( $exclude_date_after );
180 $before = self::parse_date( $exclude_date_before );
181
182 if ( $after && $before ) {
183 $query = array(
184 'after' => array(
185 'year' => $after['year'],
186 'month' => $after['month'],
187 'day' => $after['day'],
188 ),
189 'before' => array(
190 'year' => $before['year'],
191 'month' => $before['month'],
192 'day' => $before['day'],
193 ),
194 'inclusive' => true,
195 );
196 }
197
198 return $query;
199 }
200
201 /**
202 * Date filter query.
203 *
204 * @param string $filter_by_taxonomy_date Taxonomy date filter.
205 * @param string $specific_date Specific date.
206 * @param string $specific_month Specific month.
207 * @param string $specific_year Specific year.
208 * @param string $specific_period_after Specific period after.
209 * @param string $specific_period_before Specific period before.
210 * @param string $specific_date_before Specific date before.
211 * @param string $specific_date_after Specific date after.
212 * @param string $exclude_date_before Exclude date before.
213 * @param string $exclude_date_after Exclude date after.
214 * @return array Date query array.
215 */
216 public static function date_filter_query(
217 string $filter_by_taxonomy_date,
218 string $specific_date,
219 string $specific_month,
220 string $specific_year,
221 string $specific_period_after,
222 string $specific_period_before,
223 string $specific_date_before,
224 string $specific_date_after,
225 string $exclude_date_before,
226 string $exclude_date_after
227 ): array {
228 if ( empty( $filter_by_taxonomy_date ) ) {
229 return array();
230 }
231
232 $params = compact(
233 'specific_date',
234 'specific_month',
235 'specific_year',
236 'specific_period_after',
237 'specific_period_before',
238 'specific_date_before',
239 'specific_date_after'
240 );
241
242 $default_query = self::build_date_query( $filter_by_taxonomy_date, $params );
243 $exclude_query = 'exclude_specific_date' === $filter_by_taxonomy_date
244 ? self::build_exclude_date_query( $exclude_date_before, $exclude_date_after )
245 : array();
246
247 return array(
248 'default_query' => $default_query ? array( $default_query ) : array(),
249 'exclude_query' => $exclude_query ? array( $exclude_query ) : array(),
250 );
251 }
252 /**
253 * Calculate posts per page and offset, considering pagination and advertisements.
254 *
255 * This method determines how many posts should be displayed on the current page
256 * and calculates the correct offset for the query. It accounts for:
257 * - Pagination
258 * - Advertisements inserted between posts (which reduce available post slots)
259 * - Special slider blocks where pagination is usually disabled
260 *
261 * @param array $query_data Query data, usually passed from the block's frontend request.
262 * @param string $block_name The name of the block (e.g., 'post-carousel').
263 * @param int $all_post_limit The total number of posts available in the query.
264 * @return array {
265 * @type int $post_per_page Number of posts to show on the current page.
266 * @type int $offset Number of posts to skip before fetching.
267 * }
268 */
269 private static function calculate_posts_per_page( array $query_data, string $block_name, int $all_post_limit ): array {
270 // The total post limit allowed for the query.
271 $post_limit = (int) ( $query_data['postLimit'] ?? 20 );
272
273 // Current page number for pagination (defaults to page 1).
274 $current_page = (int) ( $query_data['currentPage'] ?? 1 );
275
276 // Any manual offset to start from (defaults to 0).
277 $offset = (int) ( $query_data['offset'] ?? 0 );
278
279 // Whether advertisements are displayed between posts.
280 $display_advertisement = $query_data['displayAdvertisement'] ?? false;
281
282 // The post position where the first advertisement appears.
283 $ads_number = (int) ( $query_data['adsNumber'] ?? 0 );
284
285 // How many advertisements to insert.
286 $ads_count = (int) ( $query_data['adsCount'] ?? 0 );
287
288 // Whether pagination is enabled for this block.
289 $pagination_enable = $query_data['paginationEnable'] ?? true;
290
291 // Block names that are sliders (usually show all posts without pagination).
292 $slider_block_names = array(
293 'post-carousel',
294 'post-slider',
295 'post-slider-two',
296 'thumbnail-slider',
297 'thumbnail-slider-two',
298 'post-timeline-three',
299 );
300
301 // By default, set posts per page equal to the post limit.
302 // For sliders, pagination is ignored, so they also use $post_limit.
303 $post_per_page = $post_limit;
304
305 // Advertisement adjustment logic.
306 if ( $display_advertisement ) {
307 // Check if current page contains the ad position.
308 if (
309 $post_per_page * $current_page >= $ads_number &&
310 $ads_number > $post_per_page * ( $current_page - 1 )
311 ) {
312 // Reduce available post slots if ads are inserted into the current page.
313 $post_per_page = $post_per_page > $ads_count ? $post_per_page - $ads_count : $post_per_page;
314
315 // Adjust offset if ads exactly match available slots.
316 if ( $ads_count === $post_per_page ) {
317 $offset -= $ads_count;
318 }
319 } elseif ( $post_per_page * $current_page > $ads_number ) {
320 $offset -= $ads_count;
321 }
322 }
323
324 // Calculate total pages based on all posts and posts per page.
325 $total_pages = (int) ceil( $all_post_limit / max( 1, (int) $post_per_page ) );
326
327 // Calculate final offset for the query.
328 $offset = ( ( $current_page - 1 ) * $post_per_page ) + $offset;
329
330 return array(
331 'post_per_page' => $post_per_page,
332 'offset' => $offset,
333 );
334 }
335
336
337 /**
338 * Build taxonomy query.
339 *
340 * @param array $taxonomies Taxonomy data.
341 * @param string $relation Relation type.
342 * @param string $tax_field_type Taxonomy field type.
343 * @return array Taxonomy query array.
344 */
345 private static function build_taxonomy_query( array $taxonomies, string $relation, $tax_field_type = 'term_id' ): array {
346 $tax_query = array();
347 foreach ( $taxonomies as $taxonomy ) {
348 $type = is_array( $taxonomy ) ? ( $taxonomy['type'] ?? '' ) : ( $taxonomy->type ?? '' );
349 $value = is_array( $taxonomy ) ? ( $taxonomy['value'] ?? '' ) : ( $taxonomy->value ?? '' );
350 $operator = is_array( $taxonomy ) ? ( $taxonomy['operator'] ?? 'IN' ) : ( $taxonomy->operator ?? 'IN' );
351 if ( ! empty( $type ) && ! empty( $value ) ) {
352 $tax_query[] = array(
353 'taxonomy' => $type,
354 'terms' => $value,
355 'operator' => $operator,
356 'field' => $tax_field_type,
357 );
358 }
359 }
360 if ( count( $tax_query ) > 1 ) {
361 $tax_query['relation'] = strtolower( $relation );
362 }
363 return $tax_query;
364 }
365
366 /**
367 * Build meta query for custom fields.
368 *
369 * @param array $custom_fields Custom field data.
370 * @param string $relation Relation type.
371 * @return array Meta query array.
372 */
373 private static function build_meta_query( array $custom_fields, string $relation ): array {
374 $meta_query = array();
375 foreach ( $custom_fields as $field ) {
376 $key = is_array( $field ) ? ( $field['key'] ?? '' ) : ( $field->key ?? '' );
377 $value = is_array( $field ) ? ( $field['value'] ?? '' ) : ( $field->value ?? '' );
378 $operator = is_array( $field ) ? ( $field['operator'] ?? '' ) : ( $field->operator ?? '' );
379 $type = is_array( $field ) ? ( $field['type'] ?? 'CHAR' ) : ( $field->type ?? 'CHAR' );
380 if ( ! empty( $key ) && ! empty( $value ) && ! empty( $operator ) ) {
381 $meta_query[] = array(
382 'key' => $key,
383 'value' => $value,
384 'compare' => $operator,
385 'type' => $type,
386 );
387 }
388 }
389
390 return $meta_query;
391 }
392
393 /**
394 * Build product-specific query.
395 *
396 * @param string $post_type Post type.
397 * @param string $filter_product Product filter type.
398 * @return array Product query arguments.
399 */
400 private static function build_product_query( string $post_type, string $filter_product ) {
401 $query = array();
402 if ( 'product' !== $post_type || 'none' === $filter_product ) {
403 return $query;
404 }
405 switch ( $filter_product ) {
406 case 'recent':
407 $query = array(
408 'orderby' => 'date',
409 'order' => 'DESC',
410 );
411 break;
412 case 'featured':
413 $query['tax_query'][] = array(
414 'taxonomy' => 'product_visibility',
415 'field' => 'name',
416 'terms' => 'featured',
417 'operator' => 'IN',
418 );
419 break;
420 case 'on_sale':
421 $query['meta_query'][] = array(
422 'key' => '_sale_price',
423 'value' => 0,
424 'compare' => '>',
425 'type' => 'numeric',
426 );
427 break;
428 case 'best_selling':
429 $query['meta_query'][] = array(
430 'key' => 'total_sales',
431 'value' => 0,
432 'compare' => '>',
433 );
434 $query['orderby'] = 'meta_value_num';
435 break;
436 case 'top_rated':
437 $query['meta_query'][] = array(
438 'key' => '_wc_average_rating',
439 'value' => 0,
440 'compare' => '>',
441 );
442 break;
443 case 'out_of_stock':
444 $query['meta_query'][] = array(
445 'key' => '_stock_status',
446 'value' => 'outofstock',
447 );
448 break;
449 }
450 return $query;
451 }
452
453 /**
454 * Build AJAX live filter query.
455 *
456 * @param array $ajax_live_filter AJAX live filter data.
457 * @param string $relation Relation type.
458 * @return array AJAX filter query arguments.
459 */
460 private static function build_ajax_filter_query( array $ajax_live_filter, string $relation ): array {
461 $query = array( 'tax_query' => array() );
462 foreach ( $ajax_live_filter as $filter ) {
463 $type = is_array( $filter ) ? ( $filter['type'] ?? '' ) : ( $filter->type ?? '' );
464 $id = is_array( $filter ) ? ( $filter['id'] ?? '' ) : ( $filter->id ?? '' );
465 $taxonomy_type = is_array( $filter ) ? ( $filter['taxonomy_type'] ?? '' ) : ( $filter->taxonomy_type ?? '' );
466 switch ( $type ) {
467 case 'taxonomy':
468 if ( 'all' !== $id ) {
469 $query['tax_query'][] = array(
470 'taxonomy' => $taxonomy_type,
471 'field' => 'term_id',
472 'terms' => array( $id ),
473 );
474 }
475 break;
476 case 'author':
477 if ( 'all' !== $id ) {
478 $query['author__in'] = $id;
479 }
480 break;
481 case 'order_by':
482 if ( 'all' !== $id ) {
483 $query['orderby'] = $id;
484 }
485 break;
486 case 'order':
487 if ( 'all' !== $id ) {
488 $query['order'] = $id;
489 }
490 break;
491 }
492 }
493 if ( $query['tax_query'] ) {
494 $query['tax_query']['relation'] = strtolower( $relation );
495 } else {
496 unset( $query['tax_query'] );
497 }
498 return $query;
499 }
500 /**
501 * Get URL search parameters prefixed with '_'.
502 *
503 * @return array Associative array of URL search parameters.
504 */
505 private static function get_url_search_params() {
506 $params = array();
507 $prefixes = array( 'sps_', 'tx_' );
508 // phpcs:ignore WordPress.Security.NonceVerification.Recommended
509 foreach ( $_GET as $key => $val ) {
510 if ( ! is_scalar( $val ) ) {
511 continue;
512 }
513 // Check if key starts with either 'sps_' or 'tx_'.
514 if ( 0 === strpos( $key, 'sps_' ) || 0 === strpos( $key, 'tx_' ) ) {
515 $key = sanitize_key( $key );
516 } else {
517 continue;
518 }
519 $clean_key = $key;
520 $clean_val = wp_unslash( sanitize_text_field( $val ) );
521 if ( ! empty( $clean_key ) && ! empty( $clean_val ) ) {
522 $params[ $clean_key ] = $clean_val;
523 }
524 }
525
526 return $params;
527 }
528
529 /**
530 * Post query function.
531 *
532 * @param array $query_data Query data.
533 * @param string|null $type Query type.
534 * @param string|null $block_id Block id.
535 * @return array Query results or arguments.
536 */
537 public static function query( $query_data, $type = null, $block_id = '' ) {
538 $ajax_live_filter = $query_data['ajaxLiveFilter'] ?? false;
539 $url_params = array();
540 $live_sort = false;
541 $tax_field_type = 'term_id';
542 $relation = $query_data['relation'] ?? 'and';
543 if ( ! $ajax_live_filter ) {
544 // phpcs:ignore WordPress.Security.NonceVerification.Recommended
545 $block = isset( $_GET['block'] ) ? sanitize_text_field( wp_unslash( $_GET['block'] ) ) : '';
546 if ( $block === $block_id ) {
547 $taxonomies = array();
548 $author_filter = array();
549 $url_params = self::get_url_search_params();
550
551 foreach ( $url_params as $key => $value ) {
552 if ( 0 === strpos( $key, 'tx_' ) ) {
553 $tax_type = str_replace( 'tx_', '', $key );
554 $current_taxonomy = array(
555 'id' => time() + wp_rand( 1, 999 ),
556 'type' => sanitize_key( $tax_type ),
557 'value' => is_array( $value ) ? array_map( 'sanitize_text_field', $value ) : array( sanitize_text_field( $value ) ),
558 'operator' => 'IN',
559 'initialOpen' => true,
560 );
561 if ( ! empty( $value ) ) {
562 $taxonomies[] = $current_taxonomy;
563 }
564 $tax_field_type = 'slug';
565 }
566 if ( 'sps_author' === $key ) {
567 if ( ! empty( $value ) ) {
568 $author_filter[] = array(
569 'id' => time() + wp_rand( 1, 999 ),
570 'type' => 'author',
571 'value' => $value,
572 );
573 }
574 }
575 }
576 if ( ! empty( $url_params['sps_search'] ) ) {
577 $query_data['keywordSearch'] = sanitize_text_field( $url_params['sps_search'] );
578 }
579
580 if ( ! empty( $url_params['sps_page'] ) ) {
581 $query_data['currentPage'] = absint( $url_params['sps_page'] );
582 }
583
584 if ( ! empty( $taxonomies ) ) {
585 $query_data['taxonomies'] = $taxonomies;
586 }
587 if ( ! empty( $author_filter ) ) {
588 $query_data['filterByAuthor'] = $author_filter;
589 }
590 if ( ! empty( $url_params['sps_sort'] ) ) {
591 $sps_sort = $url_params['sps_sort'];
592 // Match format like: title_asc or date_des.
593 if ( preg_match( '/^([a-zA-Z0-9_]+)_(asc|des)$/i', $sps_sort, $matches ) ) {
594 $query_data['fl_orderBy'] = sanitize_key( $matches[1] );
595 $query_data['fl_orderDirection'] = 'ASC' === strtoupper( $matches[2] ) ? 'ASC' : 'DESC';
596 // Match values like: ASC or DESC directly.
597 } elseif ( in_array( strtoupper( $sps_sort ), array( 'ASC', 'DESC' ), true ) ) {
598 $query_data['fl_orderDirection'] = strtoupper( $sps_sort );
599 } else {
600 $query_data['fl_orderBy'] = sanitize_key( $sps_sort );
601 }
602 $live_sort = true;
603 }
604 if ( isset( $query_data['multipleFilterRelation'] ) && ! empty( $query_data['multipleFilterRelation'] ) ) {
605 $relation = $query_data['multipleFilterRelation'] ?? 'and';
606 }
607 }
608 }
609 // Default query parameters.
610 $post_type = $query_data['postType'] ?? 'post';
611 $current_post_id = $query_data['postId'] ?? false;
612 $block_name = $query_data['blockName'] ?? 'post-carousel';
613 // $post_limit = (int) ( $query_data['postLimit'] ?? 20 );
614 $post_limit = (int) ( ! empty( $query_data['postLimit'] ) ? $query_data['postLimit'] : 8 );
615 $order_by = $query_data['orderBy'] ?? 'date';
616 $show_pagination = $query_data['paginationEnable'] ?? false;
617 $order_direction = $query_data['orderDirection'] ?? 'DESC';
618
619 $is_multiple = ( 'multiple_post_type' === $post_type && ! empty( $query_data['multiplePostType'] ) );
620
621 $multiple_post_type = $is_multiple
622 ? array_map(
623 function ( $type ) {
624 return is_array( $type ) ? $type['value']
625 : ( is_object( $type ) ? $type->value : $type );
626 },
627 $query_data['multiplePostType']
628 )
629 : array( $post_type );
630
631 // Calculate total post limit.
632 $all_post_limit = 0;
633 foreach ( $multiple_post_type as $value ) {
634 $all_post_limit += (int) ( wp_count_posts( $value )->publish ?? 0 );
635 }
636
637 // Calculate posts per page and offset.
638 $pagination = self::calculate_posts_per_page( $query_data, $block_name, $all_post_limit );
639 $post_per_page = $pagination['post_per_page'];
640 $offset = $pagination['offset'];
641
642 // Base query arguments.
643 $args = array(
644 'post_type' => $multiple_post_type,
645 'posts_per_page' => $post_per_page,
646 'offset' => $offset,
647 'orderby' => $order_by,
648 'order' => $order_direction,
649 'post_status' => in_array( 'attachment', $multiple_post_type, true ) ? array( 'publish', 'inherit' ) : 'publish',
650 'ignore_sticky_posts' => true,
651 );
652 if ( ! empty( $query_data['s'] ) ) {
653 $args['s'] = $query_data['s'];
654 }
655 // Date filter.
656 $date_query = self::date_filter_query(
657 $query_data['filterByDate'] ?? '',
658 $query_data['specificDate'] ?? '',
659 $query_data['specificMonth'] ?? '1',
660 $query_data['specificYear'] ?? '2024',
661 $query_data['specificPeriodAfter'] ?? '',
662 $query_data['specificPeriodBefore'] ?? '',
663 $query_data['specificDateBefore'] ?? '',
664 $query_data['specificDateAfter'] ?? '',
665 $query_data['excludeDateBefore'] ?? '',
666 $query_data['excludeDateAfter'] ?? ''
667 );
668 if ( ! empty( $date_query['default_query'] ) ) {
669 $args['date_query'] = $date_query['default_query'];
670 }
671
672 // Exclude settings.
673 if ( $query_data['excludeProtectedPosts'] ?? false ) {
674 $args['has_password'] = false;
675 }
676 if ( $query_data['excludeChildrenPosts'] ?? false ) {
677 $args['post_parent__in'] = array( 0 );
678 }
679
680 // Popular posts.
681 $popular_post = $query_data['quickQuery'] ?? '';
682 if ( $popular_post ) {
683 if ( 'popular_posts' === $popular_post ) {
684 $args['meta_key'] = '_post_views_count';
685 $args['orderby'] = 'meta_value_num';
686 } else {
687 $args = array_merge(
688 $args,
689 array(
690 'meta_key' => '_post_views_count',
691 'orderby' => 'meta_value_num',
692 'date_query' => array(
693 array(
694 'after' => $popular_post . ' ago',
695 'inclusive' => false,
696 ),
697 ),
698 )
699 );
700 }
701 }
702
703 // Taxonomy filter.
704 $taxonomies = $query_data['taxonomies'] ?? array();
705 $tax_field_type = ! empty( $query_data['tax_type'] ) ? $query_data['tax_type'] : $tax_field_type;
706 $tax_query = self::build_taxonomy_query( $taxonomies, $relation, $tax_field_type );
707 if ( ! empty( $tax_query ) ) {
708 $args['tax_query'] = $tax_query;
709 }
710
711 // Custom fields filter.
712 $custom_fields = $query_data['filterByCustomFields'] ?? array();
713 $custom_field_relation = $query_data['customFieldRelation'] ?? 'and';
714 $meta_query = self::build_meta_query( $custom_fields, $custom_field_relation );
715 // Exclude posts without images.
716 if ( $query_data['excludePostWithoutImagePosts'] ?? false ) {
717 $meta_query = array_merge(
718 $meta_query,
719 array(
720 array(
721 'key' => '_thumbnail_id',
722 'compare' => 'EXISTS',
723 ),
724 )
725 );
726 }
727 if ( $meta_query ) {
728 if ( count( $meta_query ) > 1 ) {
729 $meta_query['relation'] = strtolower( $custom_field_relation );
730 }
731 $args['meta_query'] = $meta_query;
732 }
733
734 // Author filter.
735 $author_id = $query_data['filterByAuthor'] ?? array();
736 if ( ! empty( $author_id ) ) {
737 $args['author__in'] = array_map(
738 function ( $author ) {
739 return is_array( $author ) ? $author['value'] : $author->value;
740 },
741 $author_id
742 );
743 }
744
745 // Include specific posts.
746 if ( ! empty( $query_data['includeOnlyPost'] ?? array() ) ) {
747 $args['orderby'] = 'post__in';
748 $args['post__in'] = $query_data['includeOnlyPost'];
749 }
750
751 // Exclude posts.
752 $exclude_posts = array();
753 if ( ! empty( $query_data['excludePost'] ?? array() ) ) {
754 $exclude_posts = array_merge( $exclude_posts, $query_data['excludePost'] );
755 }
756 if ( $query_data['excludeStickyPosts'] ?? false ) {
757 $exclude_posts = array_merge( $exclude_posts, get_option( 'sticky_posts', array() ) );
758 }
759 if ( $query_data['excludeCurrentPosts'] ?? true && $current_post_id ) {
760 $exclude_posts[] = $current_post_id;
761 }
762 // TODO:: need to improve Exclude date query.
763 if ( ! empty( $date_query['exclude_query'] ) ) {
764 $temp_args = array_merge(
765 $args,
766 array(
767 'date_query' => $date_query['exclude_query'],
768 'fields' => 'ids',
769 'posts_per_page' => 9999,
770 )
771 );
772 $exclude_posts = array_merge( $exclude_posts, get_posts( $temp_args ) );
773 }
774 if ( $exclude_posts ) {
775 $args['post__not_in'] = $exclude_posts;
776 }
777 // Exclude terms.
778 $exclude_term = $query_data['excludeTerm'] ?? array();
779 $exclude_cat = array();
780 $exclude_tag = array();
781 foreach ( $exclude_term as $term ) {
782 $taxonomy_type = is_array( $term ) ? ( $term['taxonomy_type'] ?? '' ) : ( $term->taxonomy_type ?? '' );
783 $value = is_array( $term ) ? ( $term['value'] ?? '' ) : ( $term->value ?? '' );
784 if ( 'category' === $taxonomy_type ) {
785 $exclude_cat[] = $value;
786 } elseif ( 'post_tag' === $taxonomy_type ) {
787 $exclude_tag[] = $value;
788 }
789 }
790 if ( $exclude_cat ) {
791 $args['category__not_in'] = $exclude_cat;
792 }
793 if ( $exclude_tag ) {
794 $args['tag__not_in'] = $exclude_tag;
795 }
796
797 // Keyword search.
798 if ( ! empty( $query_data['filterByKeyword'] ?? '' ) ) {
799 $args['s'] = $query_data['filterByKeyword'];
800 }
801 if ( ! empty( $query_data['keywordSearch'] ) ) {
802 $args['s'] = $query_data['keywordSearch'];
803 }
804
805 // Exclude authors.
806 if ( ! empty( $query_data['excludeAuthor'] ?? array() ) ) {
807 $args['author__not_in'] = $query_data['excludeAuthor'];
808 }
809
810 // Product filter.
811 $filter_product = $query_data['filterProduct'] ?? 'recent';
812 $args = array_merge( $args, self::build_product_query( $post_type, $filter_product ) );
813
814 // AJAX live filter.
815 $ajax_live_filter = $query_data['ajaxLiveFilter'] ?? false;
816
817 // Post count for AJAX.
818 if ( $ajax_live_filter ) {
819 if ( isset( $query_data['ajax_order'] ) ) {
820 if ( 'most_view' == $query_data['fl_orderBy'] ) {
821 $args['meta_key'] = '_post_views_count';
822 $args['orderby'] = 'meta_value_num';
823 $args['order'] = $query_data['fl_orderDirection'] ?? $order_direction;
824 } elseif ( 'most_like' == $query_data['fl_orderBy'] ) {
825 $args['meta_key'] = '_post_like_count';
826 $args['orderby'] = 'meta_value_num';
827 $args['order'] = $query_data['fl_orderDirection'] ?? $order_direction;
828 } else {
829 $args = array_merge(
830 $args,
831 array(
832 'orderby' => $query_data['fl_orderBy'] ?? $order_by,
833 'order' => $query_data['fl_orderDirection'] ?? $order_direction,
834 )
835 );
836 }
837 }
838 }
839 if ( $live_sort ) {
840 if ( 'most_view' == $query_data['fl_orderBy'] ) {
841 $args['meta_key'] = '_post_views_count';
842 $args['orderby'] = 'meta_value_num';
843 $args['order'] = $query_data['fl_orderDirection'] ?? $order_direction;
844 } elseif ( 'most_like' == $query_data['fl_orderBy'] ) {
845 $args['meta_key'] = '_post_like_count';
846 $args['orderby'] = 'meta_value_num';
847 $args['order'] = $query_data['fl_orderDirection'] ?? $order_direction;
848 } else {
849 $args = array_merge(
850 $args,
851 array(
852 'orderby' => $query_data['fl_orderBy'] ?? $order_by,
853 'order' => $query_data['fl_orderDirection'] ?? $order_direction,
854 )
855 );
856 }
857 }
858
859 $modify_args = array(
860 'posts_per_page' => 9999,
861 'fields' => 'ids',
862 );
863
864 $get_all_post_ids_args = wp_parse_args( $modify_args, $args );
865 $all_post_ids = get_posts( $get_all_post_ids_args );
866 $post_count = count( $all_post_ids );
867
868 $total_pages = 1;
869 if ( $post_count ) {
870 $post_limit = min( $post_count, $post_limit );
871 $total_pages = (int) ceil( $post_count / $post_limit ?? 8 );
872 }
873
874 $total_pages = max( $total_pages, 1 );
875
876 if ( 'args' === $type ) {
877 return array(
878 'args' => $args,
879 'include_posts' => $args['post__in'] ?? array(),
880 'post_limit' => $post_limit,
881 'total_pages' => $total_pages,
882 'post_ids' => $all_post_ids,
883 );
884 }
885 $wp_query_data = new \WP_Query( $args );
886
887 $result = array(
888 'the_query' => $wp_query_data,
889 'post_count' => $post_count,
890 'total_pages' => is_object( $wp_query_data ) && ! empty( $wp_query_data ) ? $total_pages : 1,
891 'post_ids' => $all_post_ids,
892 );
893 return $result;
894 }
895 }
896