PluginProbe
Ultimate Store Kit – Store Builder Addons for Elementor, WooCommerce Store Builder, EDD Store Builder / 3.0.7
Ultimate Store Kit – Store Builder Addons for Elementor, WooCommerce Store Builder, EDD Store Builder v3.0.7
3.1.4 3.0.8 3.0.9 3.1.0 3.1.2 3.1.3 3.0.7 3.0.5 3.0.4 3.0.3 3.0.2 trunk 1.5.0 1.5.1 1.5.2 1.6.1 1.6.2 1.6.3 1.6.4 2.0.0 2.0.1 2.0.2 2.0.3 2.0.4 2.0.5 All 93 releases
ultimate-store-kit / includes / controls / group-query / group-control-query.php

group-control-query.php in Ultimate Store Kit – Store Builder Addons for Elementor, WooCommerce Store Builder, EDD Store Builder 3.0.7, at includes/controls/group-query/group-control-query.php

1,015 lines 27.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace UltimateStoreKit\Includes\Controls\GroupQuery;
4
5 use Elementor\Controls_Manager;
6 use UltimateStoreKit\Includes\Controls\SelectInput\Dynamic_Select;
7
8 if (! defined('ABSPATH')) {
9 exit; // Exit if accessed directly.
10 }
11
12 trait Group_Control_Query
13 {
14
15 public function register_query_builder_controls()
16 {
17
18 $this->add_control(
19 'product_source',
20 [
21 'label' => __('Source', 'ultimate-store-kit'),
22 'type' => Controls_Manager::SELECT,
23 'options' => $this->getGroupControlQueryPostTypes(),
24 'default' => 'product',
25 ]
26 );
27 $this->add_control(
28 'product_limit',
29 [
30 'label' => esc_html__('Product Limit', 'ultimate-store-kit'),
31 'type' => Controls_Manager::NUMBER,
32 'default' => 9,
33 ]
34 );
35
36 $this->add_control(
37 'product_selected_ids',
38 [
39 'label' => __('Search & Select', 'ultimate-store-kit'),
40 'type' => Dynamic_Select::TYPE,
41 'multiple' => true,
42 'label_block' => true,
43 'query_args' => [
44 'query' => 'posts',
45 'post_type' => 'product',
46 ],
47 'condition' => [
48 'product_source' => 'manual_selection',
49 ]
50 ]
51 );
52
53 $this->start_controls_tabs(
54 'tabs_product_include_exclude',
55 [
56 'condition' => [
57 'product_source!' => ['manual_selection', 'current_query'],
58 ]
59 ]
60 );
61
62 $this->start_controls_tab(
63 'tab_product_include',
64 [
65 'label' => __('Include', 'ultimate-store-kit'),
66 'condition' => [
67 'product_source!' => ['manual_selection'],
68 ]
69 ]
70 );
71
72 $this->add_control(
73 'product_include_by',
74 [
75 'label' => __('Include By', 'ultimate-store-kit'),
76 'type' => Controls_Manager::SELECT2,
77 'multiple' => true,
78 'label_block' => true,
79 'options' => [
80 'categories' => __('Categories', 'ultimate-store-kit'),
81 'tags' => __('Tags', 'ultimate-store-kit'),
82 'brands' => __('Brands', 'ultimate-store-kit'),
83 'attributes' => __('Attributes', 'ultimate-store-kit'),
84 'date' => __('Date', 'ultimate-store-kit'),
85 'image' => __('Product Image', 'ultimate-store-kit'),
86 'price' => __('Price', 'ultimate-store-kit'),
87 'stock' => __('Stock', 'ultimate-store-kit'),
88 ],
89 'condition' => [
90 'product_source!' => ['manual_selection'],
91 ]
92 ]
93 );
94
95 $this->add_control(
96 'product_include_category_ids',
97 [
98 'label' => __('Categories', 'ultimate-store-kit'),
99 'type' => Dynamic_Select::TYPE,
100 'multiple' => true,
101 'label_block' => true,
102 'query_args' => [
103 'query' => 'product_cat',
104 ],
105 'condition' => [
106 'product_include_by' => 'categories',
107 'product_source!' => ['manual_selection'],
108 ]
109 ]
110 );
111
112 $this->add_control(
113 'product_include_tag_ids',
114 [
115 'label' => __('Tags', 'ultimate-store-kit'),
116 'type' => Dynamic_Select::TYPE,
117 'multiple' => true,
118 'label_block' => true,
119 'query_args' => [
120 'query' => 'product_tag',
121 ],
122 'condition' => [
123 'product_include_by' => 'tags',
124 'product_source!' => ['manual_selection'],
125 ]
126 ]
127 );
128
129 $this->add_control(
130 'product_include_brand_ids',
131 [
132 'label' => __('Brands', 'ultimate-store-kit'),
133 'type' => Dynamic_Select::TYPE,
134 'multiple' => true,
135 'label_block' => true,
136 'query_args' => [
137 'query' => 'product_brand',
138 ],
139 'condition' => [
140 'product_include_by' => 'brands',
141 'product_source!' => ['manual_selection'],
142 ]
143 ]
144 );
145
146 $this->add_control(
147 'product_include_attribute_ids',
148 [
149 'label' => __('Attributes', 'ultimate-store-kit'),
150 'type' => Dynamic_Select::TYPE,
151 'multiple' => true,
152 'label_block' => true,
153 'query_args' => [
154 'query' => 'product_attributes',
155 ],
156 'condition' => [
157 'product_include_by' => 'attributes',
158 'product_source!' => ['manual_selection'],
159 ]
160 ]
161 );
162
163 $this->add_control(
164 'product_include_date',
165 [
166 'label' => __('Date', 'ultimate-store-kit'),
167 'type' => Controls_Manager::SELECT,
168 'default' => 'anytime',
169 'options' => [
170 'anytime' => __('All', 'ultimate-store-kit'),
171 'today' => __('Past Day', 'ultimate-store-kit'),
172 'week' => __('Past Week', 'ultimate-store-kit'),
173 'month' => __('Past Month', 'ultimate-store-kit'),
174 'quarter' => __('Past Quarter', 'ultimate-store-kit'),
175 'year' => __('Past Year', 'ultimate-store-kit'),
176 'exact' => __('Custom', 'ultimate-store-kit'),
177 ],
178 'condition' => [
179 'product_source!' => 'manual_selection',
180 'product_include_by' => 'date',
181 ]
182 ]
183 );
184
185 $this->add_control(
186 'product_date_before',
187 [
188 'label' => __('Before', 'ultimate-store-kit'),
189 'type' => Controls_Manager::DATE_TIME,
190 'description' => __('Setting a "Before" date will show all the posts published until the chosen date (inclusive).', 'ultimate-store-kit'),
191 'condition' => [
192 'product_include_date' => 'exact',
193 'product_source!' => 'manual_selection',
194 ]
195 ]
196 );
197
198 $this->add_control(
199 'product_date_after',
200 [
201 'label' => __('After', 'ultimate-store-kit'),
202 'type' => Controls_Manager::DATE_TIME,
203 'description' => __('Setting an "After" date will show all the posts published since the chosen date (inclusive).', 'ultimate-store-kit'),
204 'condition' => [
205 'product_include_date' => 'exact',
206 'product_source!' => 'manual_selection',
207 ]
208 ]
209 );
210
211 $this->add_control(
212 'product_image_include',
213 [
214 'label' => esc_html__('Product Image', 'ultimate-store-kit'),
215 'type' => \Elementor\Controls_Manager::SELECT,
216 'default' => '',
217 'options' => [
218 '' => esc_html__('Default', 'ultimate-store-kit'),
219 'set' => esc_html__('Set', 'ultimate-store-kit'),
220 'not_set' => esc_html__('Not Set', 'ultimate-store-kit'),
221 ],
222 'condition' => [
223 'product_include_by' => 'image',
224 'product_source!' => ['manual_selection'],
225 ]
226 ]
227 );
228
229 $this->add_control(
230 'product_price_include',
231 [
232 'label' => esc_html__('Price', 'ultimate-store-kit'),
233 'type' => \Elementor\Controls_Manager::SELECT,
234 'default' => '',
235 'options' => [
236 '' => esc_html__('Default', 'ultimate-store-kit'),
237 'set' => esc_html__('Set', 'ultimate-store-kit'),
238 'not_set' => esc_html__('Not Set', 'ultimate-store-kit'),
239 ],
240 'condition' => [
241 'product_include_by' => 'price',
242 'product_source!' => ['manual_selection'],
243 ]
244 ]
245 );
246
247 $this->add_control(
248 'product_stock_include',
249 [
250 'label' => esc_html__('Stock', 'ultimate-store-kit'),
251 'type' => \Elementor\Controls_Manager::SELECT,
252 'default' => '',
253 'options' => [
254 '' => esc_html__('Default', 'ultimate-store-kit'),
255 'in_stock' => esc_html__('In Stock', 'ultimate-store-kit'),
256 'out_of_stock' => esc_html__('Out of Stock', 'ultimate-store-kit'),
257 ],
258 'condition' => [
259 'product_include_by' => 'stock',
260 'product_source!' => ['manual_selection'],
261 ]
262 ]
263 );
264
265
266 $this->end_controls_tab();
267
268 $this->start_controls_tab(
269 'tab_product_exclude',
270 [
271 'label' => __('Exclude', 'ultimate-store-kit'),
272 'condition' => [
273 'product_source!' => ['manual_selection', 'current_query'],
274 ]
275 ]
276 );
277
278 $this->add_control(
279 'product_exclude_by',
280 [
281 'label' => __('Exclude By', 'ultimate-store-kit'),
282 'type' => Controls_Manager::SELECT2,
283 'multiple' => true,
284 'label_block' => true,
285 'options' => [
286 'categories' => __('Categories', 'ultimate-store-kit'),
287 'tags' => __('Tags', 'ultimate-store-kit'),
288 'brands' => __('Brands', 'ultimate-store-kit'),
289 'attributes' => __('Attributes', 'ultimate-store-kit'),
290 'manual_selection' => __('Manual Selection', 'ultimate-store-kit'),
291 'date' => __('Date', 'ultimate-store-kit'),
292 'image' => __('Product Image', 'ultimate-store-kit'),
293 'price' => __('Price', 'ultimate-store-kit'),
294 'stock' => __('Stock', 'ultimate-store-kit'),
295 ],
296 'condition' => [
297 'product_source!' => ['manual_selection'],
298 ]
299 ]
300 );
301
302 $this->add_control(
303 'posts_exclude_ids',
304 [
305 'label' => __('Search & Select', 'ultimate-store-kit'),
306 'type' => Dynamic_Select::TYPE,
307 'multiple' => true,
308 'label_block' => true,
309 'query_args' => [
310 'query' => 'posts',
311 'post_type' => 'product',
312 ],
313 'condition' => [
314 'product_source!' => ['manual_selection'],
315 'product_exclude_by' => 'manual_selection',
316 ]
317 ]
318 );
319
320 $this->add_control(
321 'product_exclude_category_ids',
322 [
323 'label' => __('Categories', 'ultimate-store-kit'),
324 'type' => Dynamic_Select::TYPE,
325 'multiple' => true,
326 'label_block' => true,
327 'query_args' => [
328 'query' => 'product_cat',
329 ],
330 'condition' => [
331 'product_exclude_by' => 'categories',
332 'product_source!' => ['manual_selection'],
333 ]
334 ]
335 );
336 $this->add_control(
337 'product_exclude_tag_ids',
338 [
339 'label' => __('Tags', 'ultimate-store-kit'),
340 'type' => Dynamic_Select::TYPE,
341 'multiple' => true,
342 'label_block' => true,
343 'query_args' => [
344 'query' => 'product_tag',
345 ],
346 'condition' => [
347 'product_exclude_by' => 'tags',
348 'product_source!' => ['manual_selection'],
349 ]
350 ]
351 );
352 $this->add_control(
353 'product_exclude_brand_ids',
354 [
355 'label' => __('Brands', 'ultimate-store-kit'),
356 'type' => Dynamic_Select::TYPE,
357 'multiple' => true,
358 'label_block' => true,
359 'query_args' => [
360 'query' => 'product_brand',
361 ],
362 'condition' => [
363 'product_exclude_by' => 'brands',
364 'product_source!' => ['manual_selection'],
365 ]
366 ]
367 );
368 $this->add_control(
369 'product_exclude_attribute_ids',
370 [
371 'label' => __('Attributes', 'ultimate-store-kit'),
372 'type' => Dynamic_Select::TYPE,
373 'multiple' => true,
374 'label_block' => true,
375 'query_args' => [
376 'query' => 'product_attributes',
377 ],
378 'condition' => [
379 'product_exclude_by' => 'attributes',
380 'product_source!' => ['manual_selection'],
381 ]
382 ]
383 );
384
385 $this->add_control(
386 'product_exclude_date',
387 [
388 'label' => __('Date', 'ultimate-store-kit'),
389 'type' => Controls_Manager::SELECT,
390 'default' => 'anytime',
391 'options' => [
392 'anytime' => __('All', 'ultimate-store-kit'),
393 'today' => __('Past Day', 'ultimate-store-kit'),
394 'week' => __('Past Week', 'ultimate-store-kit'),
395 'month' => __('Past Month', 'ultimate-store-kit'),
396 'quarter' => __('Past Quarter', 'ultimate-store-kit'),
397 'year' => __('Past Year', 'ultimate-store-kit'),
398 'exact' => __('Custom', 'ultimate-store-kit'),
399 ],
400 'condition' => [
401 'product_exclude_by' => 'date',
402 'product_source!' => ['manual_selection'],
403 ]
404 ]
405 );
406
407 $this->add_control(
408 'product_exclude_date_before',
409 [
410 'label' => __('Before', 'ultimate-store-kit'),
411 'type' => Controls_Manager::DATE_TIME,
412 'description' => __('Show all posts published until the chosen date (inclusive).', 'ultimate-store-kit'),
413 'condition' => [
414 'product_exclude_date' => 'exact',
415 'product_source!' => ['manual_selection'],
416 ]
417 ]
418 );
419
420 $this->add_control(
421 'product_exclude_date_after',
422 [
423 'label' => __('After', 'ultimate-store-kit'),
424 'type' => Controls_Manager::DATE_TIME,
425 'description' => __('Show all posts published since the chosen date (inclusive).', 'ultimate-store-kit'),
426 'condition' => [
427 'product_exclude_date' => 'exact',
428 'product_source!' => ['manual_selection'],
429 ]
430 ]
431 );
432
433 $this->add_control(
434 'product_image_exclude',
435 [
436 'label' => esc_html__('Product Image', 'ultimate-store-kit'),
437 'type' => \Elementor\Controls_Manager::SELECT,
438 'default' => '',
439 'options' => [
440 '' => esc_html__('Default', 'ultimate-store-kit'),
441 'set' => esc_html__('Set', 'ultimate-store-kit'),
442 'not_set' => esc_html__('Not Set', 'ultimate-store-kit'),
443 ],
444 'condition' => [
445 'product_exclude_by' => 'image',
446 'product_source!' => ['manual_selection'],
447 ]
448 ]
449 );
450
451 $this->add_control(
452 'product_price_exclude',
453 [
454 'label' => esc_html__('Price', 'ultimate-store-kit'),
455 'type' => \Elementor\Controls_Manager::SELECT,
456 'default' => '',
457 'options' => [
458 '' => esc_html__('Default', 'ultimate-store-kit'),
459 'set' => esc_html__('Set', 'ultimate-store-kit'),
460 'not_set' => esc_html__('Not Set', 'ultimate-store-kit'),
461 ],
462 'condition' => [
463 'product_exclude_by' => 'price',
464 'product_source!' => ['manual_selection'],
465 ]
466 ]
467 );
468
469 $this->add_control(
470 'product_stock_exclude',
471 [
472 'label' => esc_html__('Stock', 'ultimate-store-kit'),
473 'type' => \Elementor\Controls_Manager::SELECT,
474 'default' => '',
475 'options' => [
476 '' => esc_html__('Default', 'ultimate-store-kit'),
477 'in_stock' => esc_html__('In Stock', 'ultimate-store-kit'),
478 'out_of_stock' => esc_html__('Out of Stock', 'ultimate-store-kit'),
479 ],
480 'condition' => [
481 'product_exclude_by' => 'stock',
482 'product_source!' => ['manual_selection'],
483 ]
484 ]
485 );
486
487 $this->add_control(
488 'product_exclude_term_ids',
489 [
490 'label' => __('Terms', 'ultimate-store-kit'),
491 'description' => __('Terms are items in a taxonomy. The available taxonomies are: Categories, Tags, Formats and custom taxonomies.', 'ultimate-store-kit'),
492 'type' => Dynamic_Select::TYPE,
493 'multiple' => true,
494 'label_block' => true,
495 'placeholder' => __('Type and select terms', 'ultimate-store-kit'),
496 'query_args' => [
497 'query' => 'terms',
498 'widget_props' => [
499 'post_type' => 'product_source'
500 ]
501 ],
502 'condition' => [
503 'product_exclude_by' => 'terms',
504 'product_source!' => ['manual_selection'],
505 ]
506 ]
507 );
508
509 $this->end_controls_tab();
510 $this->end_controls_tabs();
511
512 $this->add_control(
513 'product_divider',
514 [
515 'type' => Controls_Manager::DIVIDER,
516 'condition' => [
517 'product_source!' => 'current_query',
518 ]
519 ]
520 );
521
522 $this->add_control(
523 'product_orderby',
524 [
525 'label' => __('Order By', 'ultimate-store-kit'),
526 'type' => Controls_Manager::SELECT,
527 'default' => 'date',
528 'options' => [
529 'title' => __('Title', 'ultimate-store-kit'),
530 'ID' => __('ID', 'ultimate-store-kit'),
531 'date' => __('Date', 'ultimate-store-kit'),
532 'author' => __('Author', 'ultimate-store-kit'),
533 'comment_count' => __('Comment Count', 'ultimate-store-kit'),
534 'menu_order' => __('Menu Order', 'ultimate-store-kit'),
535 'modified' => __('Modified', 'ultimate-store-kit'),
536 'rand' => __('Random', 'ultimate-store-kit'),
537 'price' => __('Price', 'ultimate-store-kit'),
538 'sales' => __('Sales', 'ultimate-store-kit'),
539 ],
540 'condition' => [
541 'product_source!' => ['current_query'],
542 ]
543 ]
544 );
545 $this->add_control(
546 'product_order',
547 [
548 'label' => __('Order', 'ultimate-store-kit'),
549 'type' => Controls_Manager::SELECT,
550 'default' => 'desc',
551 'options' => [
552 'asc' => __('ASC', 'ultimate-store-kit'),
553 'desc' => __('DESC', 'ultimate-store-kit'),
554 ],
555 'condition' => [
556 'product_source!' => 'current_query',
557 ]
558 ]
559 );
560 }
561
562 public function register_controls_wc_additional()
563 {
564 $this->add_control(
565 'query_id',
566 [
567 'label' => __('Query ID', 'ultimate-store-kit'),
568 'description' => __('Give your Query a custom unique id to allow server side filtering', 'ultimate-store-kit'),
569 'type' => Controls_Manager::TEXT,
570 'separator' => 'before',
571 'dynamic' => [ 'active' => true ],
572 ]
573 );
574 }
575
576 /**
577 * @return array|string[]|\WP_Post_Type[]
578 */
579 private function getGroupControlQueryPostTypes()
580 {
581
582 $post_types = [
583 'product' => __('Product', 'ultimate-store-kit'),
584 'sale' => __('On Sale', 'ultimate-store-kit'),
585 'featured' => __('Featured', 'ultimate-store-kit'),
586 'manual_selection' => __('Manual Selection', 'ultimate-store-kit'),
587 'current_query' => __('Current Query', 'ultimate-store-kit'),
588 ];
589
590 return $post_types;
591 }
592
593 public function pre_get_posts_query_filter($wp_query)
594 {
595 if ($this) {
596 $query_id = $this->get_settings('query_id');
597 do_action("ultimate_store_kit/query/{$query_id}", $wp_query, $this);
598 }
599 }
600
601 private function get_product_source_args($source)
602 {
603 $args = [];
604
605 switch ($source) {
606 case 'sale':
607 $args['meta_query'] = [
608 [
609 'key' => '_sale_price',
610 'value' => 0,
611 'compare' => '>',
612 ],
613 ];
614 break;
615 case 'featured':
616 $args['tax_query'] = [
617 [
618 'taxonomy' => 'product_visibility',
619 'field' => 'name',
620 'terms' => 'featured',
621 'operator' => 'IN',
622 ],
623 ];
624 break;
625 case 'manual_selection':
626 $selected_ids = $this->get_settings('product_selected_ids');
627 if (!empty($selected_ids)) {
628 $args['post__in'] = $selected_ids;
629 }
630 break;
631 }
632
633 return $args;
634 }
635
636 /**
637 * Apply "Include By" logic to WP_Query args.
638 */
639 private function apply_include_by_args($args, $settings)
640 {
641 $include_by = $settings['product_include_by'] ?? [];
642
643 if (!is_array($include_by)) {
644 $include_by = [$include_by];
645 }
646
647 // Categories
648 if (in_array('categories', $include_by) && !empty($settings['product_include_category_ids'])) {
649 $args['tax_query'][] = [
650 'taxonomy' => 'product_cat',
651 'field' => 'term_id',
652 'terms' => $settings['product_include_category_ids'],
653 'operator' => 'IN',
654 ];
655 }
656
657 // Tags
658 if (in_array('tags', $include_by) && !empty($settings['product_include_tag_ids'])) {
659 $args['tax_query'][] = [
660 'taxonomy' => 'product_tag',
661 'field' => 'term_id',
662 'terms' => $settings['product_include_tag_ids'],
663 'operator' => 'IN',
664 ];
665 }
666
667 // Brands
668 if (in_array('brands', $include_by) && !empty($settings['product_include_brand_ids'])) {
669 $args['tax_query'][] = [
670 'taxonomy' => 'product_brand',
671 'field' => 'term_id',
672 'terms' => $settings['product_include_brand_ids'],
673 'operator' => 'IN',
674 ];
675 }
676
677 // Attributes
678 if (in_array('attributes', $include_by) && !empty($settings['product_include_attribute_ids'])) {
679 $args['tax_query'][] = [
680 'taxonomy' => 'product_attributes',
681 'field' => 'term_id',
682 'terms' => $settings['product_include_attribute_ids'],
683 'operator' => 'IN',
684 ];
685 }
686
687 // Date
688 if (in_array('date', $include_by) && !empty($settings['product_include_date'])) {
689 $date_query = [];
690 switch ($settings['product_include_date']) {
691 case 'today':
692 $date_query['after'] = '1 day ago';
693 break;
694 case 'week':
695 $date_query['after'] = '1 week ago';
696 break;
697 case 'month':
698 $date_query['after'] = '1 month ago';
699 break;
700 case 'quarter':
701 $date_query['after'] = '3 months ago';
702 break;
703 case 'year':
704 $date_query['after'] = '1 year ago';
705 break;
706 case 'exact':
707 if (!empty($settings['product_date_after'])) {
708 $date_query['after'] = $settings['product_date_after'];
709 }
710 if (!empty($settings['product_date_before'])) {
711 $date_query['before'] = $settings['product_date_before'];
712 }
713 $date_query['inclusive'] = true;
714 break;
715 }
716 if (!empty($date_query)) {
717 $args['date_query'][] = $date_query;
718 }
719 }
720
721 // Product Image
722 if (in_array('image', $include_by) && isset($settings['product_image_include']) && $settings['product_image_include'] !== '') {
723 if ($settings['product_image_include'] === 'set') {
724 $args['meta_query'][] = [
725 'key' => '_thumbnail_id',
726 'compare' => 'EXISTS',
727 ];
728 } elseif ($settings['product_image_include'] === 'not_set') {
729 $args['meta_query'][] = [
730 'key' => '_thumbnail_id',
731 'compare' => 'NOT EXISTS',
732 ];
733 }
734 }
735
736 // Price
737 if (in_array('price', $include_by) && isset($settings['product_price_include']) && $settings['product_price_include'] !== '') {
738 if ($settings['product_price_include'] === 'set') {
739 $args['meta_query'][] = [
740 'key' => '_price',
741 'compare' => 'EXISTS',
742 ];
743 } elseif ($settings['product_price_include'] === 'not_set') {
744 $args['meta_query'][] = [
745 'key' => '_price',
746 'compare' => 'NOT EXISTS',
747 ];
748 }
749 }
750
751 // Stock
752 if (in_array('stock', $include_by) && isset($settings['product_stock_include']) && $settings['product_stock_include'] !== '') {
753 if ($settings['product_stock_include'] === 'in_stock') {
754 $args['meta_query'][] = [
755 'key' => '_stock_status',
756 'value' => 'instock',
757 'compare' => '=',
758 ];
759 } elseif ($settings['product_stock_include'] === 'out_of_stock') {
760 $args['meta_query'][] = [
761 'key' => '_stock_status',
762 'value' => 'outofstock',
763 'compare' => '=',
764 ];
765 }
766 }
767
768 return $args;
769 }
770
771 /**
772 * Apply "Exclude By" logic to WP_Query args.
773 */
774 private function apply_exclude_by_args($args, $settings)
775 {
776 $exclude_by = $settings['product_exclude_by'] ?? [];
777
778 if (!is_array($exclude_by)) {
779 $exclude_by = [$exclude_by];
780 }
781
782 // Categories
783 if (in_array('categories', $exclude_by) && !empty($settings['product_exclude_category_ids'])) {
784 $args['tax_query'][] = [
785 'taxonomy' => 'product_cat',
786 'field' => 'term_id',
787 'terms' => $settings['product_exclude_category_ids'],
788 'operator' => 'NOT IN',
789 ];
790 }
791
792 // Tags
793 if (in_array('tags', $exclude_by) && !empty($settings['product_exclude_tag_ids'])) {
794 $args['tax_query'][] = [
795 'taxonomy' => 'product_tag',
796 'field' => 'term_id',
797 'terms' => $settings['product_exclude_tag_ids'],
798 'operator' => 'NOT IN',
799 ];
800 }
801
802 // Brands
803 if (in_array('brands', $exclude_by) && !empty($settings['product_exclude_brand_ids'])) {
804 $args['tax_query'][] = [
805 'taxonomy' => 'product_brand',
806 'field' => 'term_id',
807 'terms' => $settings['product_exclude_brand_ids'],
808 'operator' => 'NOT IN',
809 ];
810 }
811
812 // Attributes
813 if (in_array('attributes', $exclude_by) && !empty($settings['product_exclude_attribute_ids'])) {
814 $args['tax_query'][] = [
815 'taxonomy' => 'product_attributes',
816 'field' => 'term_id',
817 'terms' => $settings['product_exclude_attribute_ids'],
818 'operator' => 'NOT IN',
819 ];
820 }
821
822 // Manual Selection
823 if (in_array('manual_selection', $exclude_by) && !empty($settings['posts_exclude_ids'])) {
824 $args['post__not_in'] = $settings['posts_exclude_ids'];
825 }
826
827 // Date
828 if (in_array('date', $exclude_by) && !empty($settings['product_exclude_date'])) {
829 $date_query = [];
830 switch ($settings['product_exclude_date']) {
831 case 'today':
832 $date_query['before'] = '1 day ago';
833 break;
834 case 'week':
835 $date_query['before'] = '1 week ago';
836 break;
837 case 'month':
838 $date_query['before'] = '1 month ago';
839 break;
840 case 'quarter':
841 $date_query['before'] = '3 months ago';
842 break;
843 case 'year':
844 $date_query['before'] = '1 year ago';
845 break;
846 case 'exact':
847 if (!empty($settings['product_exclude_date_after'])) {
848 $date_query['after'] = $settings['product_exclude_date_after'];
849 }
850 if (!empty($settings['product_exclude_date_before'])) {
851 $date_query['before'] = $settings['product_exclude_date_before'];
852 }
853 $date_query['inclusive'] = true;
854 break;
855 }
856 if (!empty($date_query)) {
857 $args['date_query'][] = $date_query;
858 }
859 }
860
861 // Product Image
862 if (in_array('image', $exclude_by) && isset($settings['product_image_exclude']) && $settings['product_image_exclude'] !== '') {
863 if ($settings['product_image_exclude'] === 'set') {
864 $args['meta_query'][] = [
865 'key' => '_thumbnail_id',
866 'compare' => 'NOT EXISTS',
867 ];
868 } elseif ($settings['product_image_exclude'] === 'not_set') {
869 $args['meta_query'][] = [
870 'key' => '_thumbnail_id',
871 'compare' => 'EXISTS',
872 ];
873 }
874 }
875
876 // Price
877 if (in_array('price', $exclude_by) && isset($settings['product_price_exclude']) && $settings['product_price_exclude'] !== '') {
878 if ($settings['product_price_exclude'] === 'set') {
879 $args['meta_query'][] = [
880 'key' => '_price',
881 'compare' => 'NOT EXISTS',
882 ];
883 } elseif ($settings['product_price_exclude'] === 'not_set') {
884 $args['meta_query'][] = [
885 'key' => '_price',
886 'compare' => 'EXISTS',
887 ];
888 }
889 }
890
891 // Stock
892 if (in_array('stock', $exclude_by) && isset($settings['product_stock_exclude']) && $settings['product_stock_exclude'] !== '') {
893 if ($settings['product_stock_exclude'] === 'in_stock') {
894 $args['meta_query'][] = [
895 'key' => '_stock_status',
896 'value' => 'instock',
897 'compare' => '!=',
898 ];
899 } elseif ($settings['product_stock_exclude'] === 'out_of_stock') {
900 $args['meta_query'][] = [
901 'key' => '_stock_status',
902 'value' => 'outofstock',
903 'compare' => '!=',
904 ];
905 }
906 }
907
908 return $args;
909 }
910
911 protected function getGroupControlQueryArgs()
912 {
913 $settings = $this->get_settings();
914 $source = $settings['product_source'] ?? 'product';
915
916 // If current_query is selected, return special flag
917 if ($source === 'current_query') {
918 return ['__use_global_query' => true];
919 }
920
921 $page = max(1, get_query_var('paged'), get_query_var('page'));
922 $page = absint(empty($_GET['product-page']) ? $page : $_GET['product-page']);
923 $paged = absint($page);
924
925 $args = [
926 'post_type' => 'product',
927 'paged' => $paged
928 ];
929
930 if (!empty($settings['product_limit'])) {
931 $args['posts_per_page'] = $settings['product_limit'];
932 }
933
934 $args = $args + $this->get_product_source_args($source);
935
936 // Apply "Include By" logic
937 $args = $this->apply_include_by_args($args, $settings);
938
939 // Apply "Exclude By" logic
940 $args = $this->apply_exclude_by_args($args, $settings);
941
942 // Apply Order and OrderBy
943 if (!empty($settings['product_orderby'])) {
944 switch ($settings['product_orderby']) {
945 case 'price':
946 $args['meta_key'] = '_price';
947 $args['orderby'] = 'meta_value_num';
948 break;
949 case 'sales':
950 $args['meta_key'] = 'total_sales';
951 $args['orderby'] = 'meta_value_num';
952 break;
953 default:
954 $args['orderby'] = $settings['product_orderby'];
955 }
956 }
957
958 if (!empty($settings['product_order'])) {
959 $args['order'] = $settings['product_order'];
960 }
961
962 $query_id = $this->get_settings('query_id');
963 if (! empty($query_id)) {
964 add_action('pre_get_posts', [$this, 'pre_get_posts_query_filter']);
965 }
966
967 return $args;
968 }
969
970 /**
971 * Get query - returns global query if current_query is selected, otherwise returns custom query
972 */
973 public function get_query()
974 {
975 return $this->_query;
976 }
977
978 /**
979 * Query products - handles both custom and global queries
980 * This is a helper method that can be called from widget's query_product() method
981 */
982 protected function build_query_from_args($query_args)
983 {
984 // Check if we should use global query
985 if (isset($query_args['__use_global_query']) && $query_args['__use_global_query'] === true) {
986 global $wp_query;
987
988 // Get the product limit setting
989 $settings = $this->get_settings();
990 $product_limit = $settings['product_limit'] ?? null;
991
992 // If product limit is set, create a new query based on global query args
993 if (!empty($product_limit)) {
994 $args = $wp_query->query_vars;
995 $args['posts_per_page'] = $product_limit;
996 return new \WP_Query($args);
997 }
998
999 return $wp_query;
1000 } else {
1001 return new \WP_Query($query_args);
1002 }
1003 }
1004
1005 /**
1006 * Query products - handles both custom and global queries
1007 * Default implementation - widgets can override this
1008 */
1009 public function query_product()
1010 {
1011 $query_args = $this->getGroupControlQueryArgs();
1012 $this->_query = $this->build_query_from_args($query_args);
1013 }
1014 }
1015