From f059b235d2ff84bd97a697674d7ae75b4cc06a81 Mon Sep 17 00:00:00 2001 From: Paul Kilmurray Date: Mon, 12 Jun 2023 14:07:41 +0100 Subject: [PATCH] v1.2 --- includes/API/Products.php | 89 +++++++++++++++++++++++++++++---------- includes/Init.php | 15 ++++++- package.json | 2 +- readme.txt | 9 ++-- woocommerce-pos.php | 4 +- 5 files changed, 88 insertions(+), 31 deletions(-) diff --git a/includes/API/Products.php b/includes/API/Products.php index 419c5ea..5717d03 100644 --- a/includes/API/Products.php +++ b/includes/API/Products.php @@ -11,6 +11,9 @@ use WC_Product_Query; use function is_array; +/** + * @property string $search_term + */ class Products { private $request; @@ -26,7 +29,7 @@ public function __construct( WP_REST_Request $request ) { add_filter( 'woocommerce_rest_prepare_product_object', array( $this, 'product_response' ), 10, 3 ); add_filter( 'woocommerce_rest_product_object_query', array( $this, 'product_query' ), 10, 2 ); add_filter( 'posts_search', array( $this, 'posts_search' ), 10, 2 ); - add_filter( 'posts_clauses', array( $this, 'orderby_stock_quantity' ), 10, 2 ); + add_filter( 'posts_clauses', array( $this, 'posts_clauses' ), 10, 2 ); add_filter( 'woocommerce_rest_product_schema', array( $this, 'add_barcode_to_product_schema' ) ); add_action( 'woocommerce_rest_insert_product_object', array( $this, 'insert_product_object' ), 10, 3 ); } @@ -213,22 +216,49 @@ public function product_response( WP_REST_Response $response, WC_Data $product, * @return array $args Key value array of query var to query value. */ public function product_query( array $args, WP_REST_Request $request ): array { - // Note!: date_query is removed from the query, use 'after' and delete this filter - - // $params = $request->get_query_params(); - // if ( isset( $params['date_modified_gmt_after'] ) ) { - // $date_query = array( - // 'column' => 'post_modified_gmt', - // 'after' => $params['date_modified_gmt_after'], - // ); - // array_push( $args['date_query'], $date_query ); - // // array_push( $args['after'], $date_query ); - // - // } + if ( ! empty( $request['search'] ) ) { + // We need to set the query up for a postmeta join + add_filter( 'posts_join', array( $this, 'barcode_postmeta_join' ), 10, 2 ); + add_filter( 'posts_groupby', array( $this, 'barcode_postmeta_groupby' ), 10, 2 ); + } return $args; } + + /** + * Filters the JOIN clause of the query. + * + * @param string $join The JOIN clause of the query. + * @param WP_Query $query The WP_Query instance (passed by reference). + */ + public function barcode_postmeta_join( string $join, WP_Query $query ): string { + global $wpdb; + + if ( isset( $query->query_vars['s'] ) ) { + $join .= " LEFT JOIN {$wpdb->postmeta} ON {$wpdb->posts}.ID = {$wpdb->postmeta}.post_id"; + } + + return $join; + } + + /** + * Filters the GROUP BY clause of the query. + * + * @param string $groupby The GROUP BY clause of the query. + * @param WP_Query $query The WP_Query instance (passed by reference). + */ + public function barcode_postmeta_groupby( string $groupby, WP_Query $query ): string { + global $wpdb; + + if ( ! empty( $query->query_vars['s'] ) ) { + $groupby = "{$wpdb->posts}.ID"; + } + + return $groupby; + } + + /** * Filters all query clauses at once, for convenience. * @@ -248,7 +278,7 @@ public function product_query( array $args, WP_REST_Request $request ): array { * } * @param WP_Query $wp_query The WP_Query instance (passed by reference). */ - public function orderby_stock_quantity( array $clauses, WP_Query $wp_query ): array { + public function posts_clauses( array $clauses, WP_Query $wp_query ): array { global $wpdb; // add option to order by stock quantity @@ -271,34 +301,47 @@ public function orderby_stock_quantity( array $clauses, WP_Query $wp_query ): ar /** - * Search SQL filter for matching against post title only. + * Filter to adjust the WordPress search SQL query + * - Search for the product title and SKU and barcode + * - Do not search product description * - * @param string $search + * @param string $search * @param WP_Query $wp_query */ - public function posts_search( $search, $wp_query ): string { - if ( ! empty( $search ) && ! empty( $wp_query->query_vars['search_terms'] ) ) { - global $wpdb; + public function posts_search( string $search, WP_Query $wp_query ): string { + global $wpdb; + if ( ! empty( $search ) && ! empty( $wp_query->query_vars['search_terms'] ) ) { $q = $wp_query->query_vars; $n = ! empty( $q['exact'] ) ? '' : '%'; - $search = array(); + $search_array = array(); foreach ( (array) $q['search_terms'] as $term ) { - $search[] = $wpdb->prepare( "$wpdb->posts.post_title LIKE %s", $n . $wpdb->esc_like( $term ) . $n ); + $like_term = $wpdb->esc_like( $term ); + $search_array[] = $wpdb->prepare( "{$wpdb->posts}.post_title LIKE %s", $n . $like_term . $n ); + + // Search in _sku field + $search_array[] = $wpdb->prepare( "(wp_postmeta.meta_key = '_sku' AND wp_postmeta.meta_value LIKE %s)", $n . $like_term . $n ); + + // Search in barcode field + $barcode_field = woocommerce_pos_get_settings( 'general', 'barcode_field' ); + if ( $barcode_field !== '_sku' ) { + $search_array[] = $wpdb->prepare( "(wp_postmeta.meta_key = %s AND wp_postmeta.meta_value LIKE %s)", $barcode_field, $n . $like_term . $n ); + } } if ( ! is_user_logged_in() ) { - $search[] = "$wpdb->posts.post_password = ''"; + $search_array[] = "{$wpdb->posts}.post_password = ''"; } - $search = ' AND ' . implode( ' AND ', $search ); + $search = ' AND (' . implode( ' OR ', $search_array ) . ')'; } return $search; } + /** * Returns array of all product ids, name. * diff --git a/includes/Init.php b/includes/Init.php index aa7f6cf..c1b09ce 100644 --- a/includes/Init.php +++ b/includes/Init.php @@ -123,8 +123,19 @@ public function query_vars( array $query_vars ): array { */ public function rest_pre_serve_request( bool $served, WP_HTTP_Response $result, WP_REST_Request $request, WP_REST_Server $server ): bool { if ( 'OPTIONS' == $request->get_method() ) { + $allow_headers = array( + 'Authorization', // For user-agent authentication with a server. + 'X-WP-Nonce', // WordPress-specific header, used for CSRF protection. + 'Content-Disposition', // Informs how to process the response data. + 'Content-MD5', // For verifying data integrity. + 'Content-Type', // Specifies the media type of the resource. + 'X-HTTP-Method-Override', // Used to override the HTTP method. + 'X-WCPOS', // Used to identify WCPOS requests. + ); + $server->send_header( 'Access-Control-Allow-Origin', '*' ); - $server->send_header( 'Access-Control-Allow-Headers', 'Authorization, X-WP-Nonce, Content-Disposition, Content-MD5, Content-Type, X-WCPOS' ); + $server->send_header( 'Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH, DELETE' ); + $server->send_header( 'Access-Control-Allow-Headers', implode( ', ', $allow_headers ) ); } return $served; @@ -159,7 +170,7 @@ private function integrations(): void { * It is programmatically turned off here for POS requests * This gets loaded and cached before the rest_api init hook, so we can't use the filter */ - public function remove_wpseo_rest_api_links ( $wpseo_options ) { + public function remove_wpseo_rest_api_links( $wpseo_options ) { if ( woocommerce_pos_request() ) { $wpseo_options['remove_rest_api_links'] = true; $wpseo_options['enable_headless_rest_endpoints'] = false; diff --git a/package.json b/package.json index e52ca44..f037f00 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@wcpos/woocommerce-pos", - "version": "1.1.0", + "version": "1.2.0", "description": "A simple front-end for taking WooCommerce orders at the Point of Sale.", "main": "index.js", "workspaces": { diff --git a/readme.txt b/readme.txt index 4c85817..d46b027 100644 --- a/readme.txt +++ b/readme.txt @@ -3,7 +3,7 @@ Contributors: kilbot Tags: cart, e-commerce, ecommerce, inventory, point-of-sale, pos, sales, sell, shop, shopify, store, vend, woocommerce, wordpress-ecommerce Requires at least: 5.6 & WooCommerce 5.3 Tested up to: 6.2 -Stable tag: 1.1.0 +Stable tag: 1.2.0 License: GPL-3.0 License URI: http://www.gnu.org/licenses/gpl-3.0.html @@ -63,11 +63,14 @@ There is more information on our website at [https://wcpos.com](https://wcpos.co == Changelog == -= 1.2.0 - 2023/06/xx = -* Improvement: remove private meta data from Order Preview modal += 1.2.0 - 2023/06/12 = +* Refactor: data replication to improve performance +* Refactor: product search, search by sku and barcode for products now works + - Note: barcode search for variations is still not working, this will be addressed in a future release * Bug Fix: 'Cannot use object of type Closure as array' in the API.php file * Bug Fix: Creating orders with decimal quantity * Bug Fix: Update product with decimal quantity +* Fix: remove private meta data from Order Preview modal * Fix: turn off PHP version check by composer, note that PHP 7.2+ is still required = 1.1.0 - 2023/05/19 = diff --git a/woocommerce-pos.php b/woocommerce-pos.php index 4019462..0e8278c 100644 --- a/woocommerce-pos.php +++ b/woocommerce-pos.php @@ -3,7 +3,7 @@ * Plugin Name: WooCommerce POS * Plugin URI: https://wordpress.org/plugins/woocommerce-pos/ * Description: A simple front-end for taking WooCommerce orders at the Point of Sale. Requires WooCommerce. - * Version: 1.1.0 + * Version: 1.2.0 * Author: kilbot * Author URI: http://wcpos.com * Text Domain: woocommerce-pos @@ -23,7 +23,7 @@ use Dotenv\Dotenv; // Define plugin constants. -const VERSION = '1.1.0'; +const VERSION = '1.2.0'; const PLUGIN_NAME = 'woocommerce-pos'; const SHORT_NAME = 'wcpos'; \define( __NAMESPACE__ . '\PLUGIN_FILE', plugin_basename( __FILE__ ) ); // 'woocommerce-pos/woocommerce-pos.php'