PluginProbe ʕ •ᴥ•ʔ
ShopEngine Elementor WooCommerce Builder Addon – All in One WooCommerce Solution / 4.8.5
ShopEngine Elementor WooCommerce Builder Addon – All in One WooCommerce Solution v4.8.5
4.9.1 4.9.0 2.0.0 2.1.0 2.2.0 2.2.1 2.2.2 2.3.0 2.4.0 2.5.0 2.5.1 3.0.0 3.1.0 3.1.1 4.0.0 4.0.1 4.1.0 4.1.1 4.2.0 4.2.1 4.3.0 4.3.1 4.4.0 4.5.0 4.5.1 4.6.0 4.6.1 4.6.2 4.6.3 4.6.4 4.6.5 4.6.6 4.6.7 4.6.8 4.6.9 4.7.0 4.7.1 4.7.2 4.7.3 4.7.4 4.7.5 4.7.6 4.7.7 4.7.8 4.7.9 4.8.0 4.8.1 4.8.2 4.8.3 4.8.4 4.8.5 4.8.6 4.8.7 4.8.8 4.8.9 trunk 0.1.2-beta 0.1.3-beta 0.1.4-beta 1.0.0 1.1.0 1.1.1 1.1.2 1.1.3 1.2.0 1.2.1 1.3.0 1.3.1 1.3.2 1.3.3 1.3.4 1.4.0 1.4.1 1.5.0 1.5.1 1.6.0 1.6.1 1.7.0 1.8.0 1.8.1 1.9.0
shopengine / core / query-modifier.php
shopengine / core Last commit date
builders 1 year ago elementor-controls 2 years ago export-import 3 years ago multi-language 3 years ago onboard 11 months ago page-templates 8 months ago register 11 months ago sample-designs 9 months ago service-providers 3 years ago settings 8 months ago theme-support 3 years ago wc-customizer 2 years ago query-modifier.php 8 months ago service-provider-manager.php 3 years ago template-cpt.php 2 years ago
query-modifier.php
224 lines
1 <?php
2
3 namespace ShopEngine\Core;
4
5 use ShopEngine\Traits\Singleton;
6 use ShopEngine\Core\Register\Widget_List;
7
8 class Query_Modifier
9 {
10
11 use Singleton;
12
13 private $custom_query = [];
14
15 public function init()
16 {
17
18 add_action('pre_get_posts', [$this, 'modify_query']);
19 }
20
21 public function modify_query($query)
22 {
23
24
25 if (is_admin() || !$query->is_main_query() || $query->is_single === true) {
26 return;
27 }
28
29 // Only proceed when it's the product query from widgets OR we're on the shop/product archive
30 $is_product_query_flag = isset($query->query_vars['wc_query']) && $query->query_vars['wc_query'] == 'product_query';
31 $is_shop_archive = ( function_exists('is_shop') && is_shop() ) || ( isset($query->query_vars['post_type']) && $query->query_vars['post_type'] === 'product' ) || ( isset($query->is_post_type_archive) && $query->is_post_type_archive === true );
32
33 if ( ! $is_product_query_flag && ! $is_shop_archive ) {
34 return;
35 }
36
37 // query filter begins
38
39 // update query for product per page filter
40 //phpcs:ignore WordPress.Security.NonceVerification.Recommended -- It's a fronted user part, not possible to verify nonce here
41 if (!empty($_GET['shopengine_products_per_page'])) {
42 //phpcs:ignore WordPress.Security.NonceVerification.Recommended
43 $query->set('posts_per_page', absint(intval($_GET['shopengine_products_per_page'])));
44 }
45
46 // checking product filter widget active or not
47 // but if `filter_pa_*` params are present on shop archive, allow processing even if widget not active
48 $has_filter_params = false;
49 if ( $is_shop_archive ) {
50 foreach ( $_GET as $k => $v ) {
51 if ( strpos( $k, 'filter_pa_' ) === 0 && ! empty( $v ) ) {
52 $taxonomy = substr( $k, strlen('filter_') ); // e.g. pa_color
53 $values = explode(',', trim( $v ));
54
55 $this->custom_query['relation'] = 'AND';
56 $this->custom_query[] = [
57 'taxonomy' => $taxonomy,
58 'field' => 'slug',
59 'terms' => $values,
60 'operator' => 'IN',
61 ];
62
63 $has_filter_params = true;
64 }
65 }
66 }
67
68 $active_widgets = Widget_List::instance()->get_list(true, 'active');
69 if (!isset($active_widgets['product-filters']) && !$has_filter_params) {
70 return;
71 }
72
73 $color_prefix = 'shopengine_filter_color_';
74
75 $attribute_prefix = 'shopengine_filter_attribute_';
76
77 $image_prefix = 'shopengine_filter_image_';
78
79 $label_prefix = 'shopengine_filter_label_';
80
81 $shipping_prefix = 'shopengine_filter_shipping_';
82
83 $category_prefix = 'shopengine_filter_category';
84
85 $stock_prefix = 'shopengine_filter_stock';
86
87 $sale_prefix = 'shopengine_filter_onsale';
88
89 $brand_prefix = 'shopengine_filter_brand';
90
91 $meta_query = ['relation' => 'AND'];
92
93 //phpcs:ignore WordPress.Security.NonceVerification.Recommended -- It's a fronted user part, not possible to verify nonce here
94 foreach ($_GET as $key => $value) {
95
96 if ($key === 'rating_filter') {
97 $meta_query[] = [
98 'key' => '_wc_average_rating',
99 'value' => explode(',', trim($value)),
100 'type' => 'numeric',
101 'compare' => 'IN'
102 ];
103 }
104
105 if ($key === $category_prefix) {
106
107 $query->query['product_cat'] = '';
108 $query->query_vars['product_cat'] = '';
109 $query = $this->query($key . 'product_cat', $category_prefix, $value, $query);
110
111 } elseif (strpos($key, $color_prefix) !== false) {
112
113 $query = $this->query($key, $color_prefix, $value, $query);
114
115 } elseif (strpos($key, $attribute_prefix) !== false) {
116
117 $query = $this->query($key, $attribute_prefix, $value, $query);
118
119 } elseif (strpos($key, $image_prefix) !== false) {
120
121 $query = $this->query($key, $image_prefix, $value, $query);
122
123 } elseif (strpos($key, $label_prefix) !== false) {
124
125 $query = $this->query($key, $label_prefix, $value, $query);
126
127 } elseif (strpos($key, $shipping_prefix) !== false) {
128
129 $query = $this->query($key, $shipping_prefix, $value, $query);
130
131 } elseif ($key === $brand_prefix) {
132
133 $query = $this->query($key, $brand_prefix, $value, $query);
134
135 } elseif ($key === $stock_prefix) {
136
137 $meta_query[] = [
138 'key' => '_stock_status',
139 'value' => $value,
140 'compare' => 'IN'
141 ];
142
143
144 } elseif ($key === $sale_prefix) {
145
146 $s = explode(',', $value);
147
148 foreach ($s as $v) {
149
150 if ($v === 'on_sale') {
151
152 $product_ids_on_sale = wc_get_product_ids_on_sale(); // including varriation products
153 $query->set( 'post__in', (array) $product_ids_on_sale );
154
155 } else {
156 $meta_query[] = [
157 'key' => '_sale_price',
158 'compare' => 'NOT EXISTS',
159 'operator' => 'OR',
160 ];
161 }
162 }
163 }
164
165 // Support custom attribute filtering (filter_custom_{slug})
166 elseif (strpos($key, 'filter_custom_') === 0) {
167 $attr_slug = substr($key, strlen('filter_custom_')); // e.g. material, brand
168 $values = array_map('sanitize_text_field', explode(',', trim($value)));
169
170 // Search in serialized _product_attributes meta for custom attributes
171 foreach ($values as $val) {
172 $meta_query[] = [
173 'key' => '_product_attributes',
174 'value' => '"' . $val . '"',
175 'compare' => 'LIKE'
176 ];
177 }
178 }
179 }
180
181 if (!empty($meta_query)) {
182 $query->set('meta_query', $meta_query);
183 }
184
185 $product_visibility_terms = wc_get_product_visibility_term_ids();
186 $product_visibility_not_in = array( $product_visibility_terms['exclude-from-catalog'] );
187
188 if ( 'yes' === get_option( 'woocommerce_hide_out_of_stock_items' ) ) {
189 $product_visibility_not_in[] = $product_visibility_terms['outofstock'];
190 }
191 $this->custom_query['tax_query'][] = apply_filters('shopengine-product-visibility-modifier',[
192 'taxonomy' => 'product_visibility',
193 'terms' => $product_visibility_not_in,
194 'field' => 'term_taxonomy_id',
195 'operator' => 'NOT IN',
196 ]);
197
198 $query->set('tax_query', apply_filters('shopengine-tax-query-modifier', $this->custom_query));
199 }
200
201 public function query($key, $prefix, $values, $query)
202 {
203 // Handle brand filter specifically
204 if ($prefix === 'shopengine_filter_brand') {
205 $taxonomy = 'product_brand';
206 } else {
207 $taxonomy = str_replace($prefix, '', $key);
208 }
209
210 $values = explode(',', trim($values));
211
212 $this->custom_query['relation'] = 'AND';
213
214 $this->custom_query[] = [
215 'taxonomy' => $taxonomy,
216 'field' => 'slug',
217 'terms' => $values,
218 'operator' => 'IN',
219 ];
220
221 return $query;
222 }
223 }
224