PluginProbe
WCPOS – Point of Sale (POS) plugin for WooCommerce / 1.10.17
WCPOS – Point of Sale (POS) plugin for WooCommerce v1.10.17
1.10.25 1.10.24 1.10.23 1.10.22 1.10.21 1.10.20 1.10.19 1.10.18 1.10.17 1.10.16 1.10.15 1.10.13 1.10.14 1.10.12 1.10.11 1.10.10 1.10.9 1.10.8 untagged-3d9b7ccddc54df87c672 1.10.7 1.10.6 1.10.5 1.10.3 1.10.4 1.10.2 All 169 releases
← All changes | includes/API/V1/Product_Variations_Controller.php +72 -36 1.10.18 → 1.10.17 View file →
@@ -20,9 +20,8 @@
20 20 use WCPOS\WooCommercePOS\Services\Barcode_Field;
21 21 use WCPOS\WooCommercePOS\Sync\Collection_Rules;
22 22 use WCPOS\WooCommercePOS\Sync\Collection_Rules_Plan;
23 23 use WCPOS\WooCommercePOS\Sync\Pos_Visibility;
24 -use WCPOS\WooCommercePOS\Sync\Product_Search;
25 24 use WP_Error;
26 25 use WP_Query;
27 26 use WP_REST_Request;
28 27 use WP_REST_Response;
@@ -64,9 +63,8 @@
64 63 */
65 64 private const WCPOS_SORT_PARAM_MAP = array(
66 65 'orderby' => 'orderby',
67 66 'order' => 'order',
68 - 'search' => 'search',
69 67 );
70 68
71 69 /**
72 70 * Dispatch request to parent controller, or override if needed.
@@ -81,8 +79,10 @@
81 79
82 80 add_filter( 'woocommerce_rest_prepare_product_variation_object', array( $this, 'wcpos_variation_response' ), 10, 3 );
83 81 add_action( 'woocommerce_rest_insert_product_variation_object', array( $this, 'wcpos_insert_product_variation_object' ), 10, 3 );
84 82 add_filter( 'woocommerce_rest_product_variation_object_query', array( $this, 'wcpos_product_variation_query' ), 10, 2 );
83 + add_filter( 'posts_search', array( $this, 'wcpos_posts_search' ), 10, 2 );
84 + add_filter( 'posts_clauses', array( $this, 'wcpos_posts_clauses' ), 10, 2 );
85 85
86 86 /*
87 87 * Check if the request is for all products and if the 'posts_per_page' is set to -1.
88 88 * Optimised query for getting all product IDs.
@@ -94,23 +94,8 @@
94 94 return $dispatch_result;
95 95 }
96 96
97 97 /**
98 - * Apply the collection's declared rules for nested and flat direct reads.
99 - *
100 - * @param WP_REST_Request $request Full details about the request.
101 - * @return \WP_Error|\WP_REST_Response
102 - */
103 - public function get_items( $request ) {
104 - $plan = Collection_Rules::for_request( 'variations', $request, self::WCPOS_SORT_PARAM_MAP );
105 - return $plan->around(
106 - function () use ( $request ) {
107 - return parent::get_items( $request );
108 - }
109 - );
110 - }
111 -
112 - /**
113 98 * Register routes.
114 99 */
115 100 public function register_routes(): void {
116 101 parent::register_routes();
@@ -200,14 +185,8 @@
200 185 if ( isset( $params['per_page'] ) && \is_array( $params['per_page'] ) ) {
201 186 $params['per_page']['minimum'] = -1;
202 187 }
203 188
204 - // Search text is literal on every lane: `sanitize_text_field` would strip `%30`
205 - // and blank malformed UTF-8 before the declared search rule ever saw them.
206 - if ( isset( $params['search'] ) && \is_array( $params['search'] ) ) {
207 - $params['search']['sanitize_callback'] = 'rest_sanitize_request_arg';
208 - }
209 -
210 189 // Ensure 'orderby' is set and is an array before attempting to modify it.
211 190 if ( isset( $params['orderby']['enum'] ) && \is_array( $params['orderby']['enum'] ) ) {
212 191 // DECLARED once, in Sync\Collection_Rules, and projected here — so a sort cannot
213 192 // be advertised on one lane and rejected on the other.
@@ -291,12 +270,51 @@
291 270 * @param string $search Search string.
292 271 * @param WP_Query $wp_query WP_Query object.
293 272 *
294 273 * @return string
295 - * @deprecated Collection Rules now installs this behavior.
296 274 */
297 275 public function wcpos_posts_search( string $search, WP_Query $wp_query ) {
298 - return Product_Search::variation_posts_search( $search, $wp_query->query_vars, Collection_Rules::rules( 'variations' )['search'] );
276 + global $wpdb;
277 +
278 + if ( empty( $search ) ) {
279 + return $search; // skip processing - no search term in query.
280 + }
281 +
282 + $q = $wp_query->query_vars;
283 + $n = ! empty( $q['exact'] ) ? '' : '%';
284 + $search_terms = (array) $q['search_terms'];
285 +
286 + // Fields in the main 'posts' table.
287 + $post_fields = array(); // nothing at the moment for variations.
288 +
289 + // Meta fields to search.
290 + $meta_fields = Barcode_Field::search_keys();
291 +
292 + $meta_placeholders = implode( ', ', array_fill( 0, \count( $meta_fields ), '%s' ) );
293 + $search_conditions = array();
294 +
295 + foreach ( $search_terms as $term ) {
296 + $term = $n . $wpdb->esc_like( $term ) . $n;
297 +
298 + // Search in meta fields.
299 + // phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Table names come from $wpdb; $meta_placeholders is a generated list of %s placeholders, and the keys themselves are passed to prepare() as arguments.
300 + $search_conditions[] = $wpdb->prepare(
301 + "EXISTS (
302 + SELECT 1 FROM {$wpdb->postmeta} AS wcpos_search_meta WHERE wcpos_search_meta.post_id = {$wpdb->posts}.ID AND wcpos_search_meta.meta_key IN ($meta_placeholders) AND wcpos_search_meta.meta_value LIKE %s
303 + )",
304 + array_merge( $meta_fields, array( $term ) )
305 + );
306 + // phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
307 + }
308 +
309 + if ( ! empty( $search_conditions ) ) {
310 + $search = ' AND (' . implode( ' AND ', $search_conditions ) . ') ';
311 + if ( ! is_user_logged_in() ) {
312 + $search .= " AND ($wpdb->posts.post_password = '') ";
313 + }
314 + }
315 +
316 + return $search;
299 317 }
300 318
301 319 /**
302 320 * Filters the JOIN clause of the query.
@@ -304,12 +322,17 @@
304 322 * @param string $join The JOIN clause of the query.
305 323 * @param WP_Query $query The WP_Query instance (passed by reference).
306 324 *
307 325 * @return string
308 - * @deprecated Collection Rules now installs this behavior.
309 326 */
310 327 public function wcpos_posts_join_to_posts_search( string $join, WP_Query $query ) {
311 - return empty( $query->query_vars['s'] ) ? $join : Product_Search::posts_join( $join, $query->query_vars );
328 + global $wpdb;
329 +
330 + if ( ! empty( $query->query_vars['s'] ) && false === strpos( $join, 'pm1' ) ) {
331 + $join .= " LEFT JOIN {$wpdb->postmeta} pm1 ON {$wpdb->posts}.ID = pm1.post_id ";
332 + }
333 +
334 + return $join;
312 335 }
313 336
314 337 /**
315 338 * Filters the GROUP BY clause of the query.
@@ -317,12 +340,17 @@
317 340 * @param string $groupby The GROUP BY clause of the query.
318 341 * @param WP_Query $query The WP_Query instance (passed by reference).
319 342 *
320 343 * @return string
321 - * @deprecated Collection Rules now installs this behavior.
322 344 */
323 345 public function wcpos_posts_groupby_posts_search( string $groupby, WP_Query $query ) {
324 - return empty( $query->query_vars['s'] ) ? $groupby : Product_Search::posts_groupby( $groupby, $query->query_vars );
346 + global $wpdb;
347 +
348 + if ( ! empty( $query->query_vars['s'] ) ) {
349 + $groupby = "{$wpdb->posts}.ID";
350 + }
351 +
352 + return $groupby;
325 353 }
326 354
327 355 /**
328 356 * Filter the query arguments for a request.
@@ -332,10 +360,21 @@
332 360 *
333 361 * @return array $args Key value array of query var to query value.
334 362 */
335 363 public function wcpos_product_variation_query( array $args, WP_REST_Request $request ) {
364 + if ( ! empty( $request['search'] ) ) {
365 + // We need to set the query up for a postmeta join.
366 + add_filter( 'posts_join', array( $this, 'wcpos_posts_join_to_posts_search' ), 10, 2 );
367 + add_filter( 'posts_groupby', array( $this, 'wcpos_posts_groupby_posts_search' ), 10, 2 );
368 + }
369 +
370 + // if POS only products are enabled, exclude online-only products.
371 + if ( $this->wcpos_pos_only_products_enabled() ) {
372 + add_filter( 'posts_where', array( $this, 'wcpos_posts_where_product_variation_exclude_online_only' ), 10, 2 );
373 + }
374 +
336 375 // Check for wcpos_include/wcpos_exclude parameter.
337 - // The Collection Rules visibility backstop runs first, at priority 10.
376 + // NOTE: do this after POS visibility filter so that takes precedence.
338 377 if ( isset( $request['wcpos_include'] ) || isset( $request['wcpos_exclude'] ) ) {
339 378 add_filter( 'posts_where', array( $this, 'wcpos_posts_where_product_variation_include_exclude' ), 20, 2 );
340 379 }
341 380
@@ -351,9 +390,8 @@
351 390 * @param string $where The WHERE clause of the query.
352 391 * @param WP_Query $query The WP_Query instance (passed by reference).
353 392 *
354 393 * @return string
355 - * @deprecated Collection Rules now installs this behavior.
356 394 */
357 395 public function wcpos_posts_where_product_variation_exclude_online_only( string $where, WP_Query $query ) {
358 396 global $wpdb;
359 397
@@ -435,9 +473,9 @@
435 473 *
436 474 * @param WP_REST_Request $request Full details about the request.
437 475 */
438 476 public function wcpos_get_all_items( $request ) {
439 - return $this->get_items( $request );
477 + return parent::get_items( $request );
440 478 }
441 479
442 480
443 481 /**
@@ -450,9 +488,8 @@
450 488 * @param array $clauses Associative array of the clauses for the query.
451 489 * @param WP_Query $wp_query The WP_Query instance.
452 490 *
453 491 * @return array
454 - * @deprecated Collection Rules now installs this behavior.
455 492 */
456 493 public function wcpos_posts_clauses( array $clauses, WP_Query $wp_query ): array {
457 494 if ( ! isset( $this->wcpos_request ) ) {
458 495 return $clauses;
@@ -481,12 +518,11 @@
481 518 /*
482 519 * The POS sorts (`sku`, `barcode`, `stock_quantity`, `stock_status`) are NOT mapped
483 520 * onto `meta_key` + `orderby => meta_value` here any more. That pair INNER JOINs
484 521 * postmeta, so it dropped every variation with no value for the key — a sort acting
485 - * as a filter. `Sync\Collection_Rules` declares them and its scoped plan
522 + * as a filter. `Sync\Collection_Rules` declares them and `wcpos_posts_clauses()`
486 523 * applies them as a LEFT JOIN, on this lane and on `wcpos/v2` alike.
487 524 */
488 525
489 - $plan = Collection_Rules::for_request( 'variations', $request, self::WCPOS_SORT_PARAM_MAP );
490 - return $plan->filter( Collection_Rules_Plan::HOOK_PREPARE_ARGS, $args );
526 + return $args;
491 527 }
492 528 }