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