Include sku partial match for products search.

This commit is contained in:
Jaclyn Chen 2022-03-09 13:59:27 +08:00 committed by Josh Betz
parent 9a71334fb1
commit 532c580081
2 changed files with 127 additions and 0 deletions

View File

@ -25,6 +25,9 @@ class WC_REST_Products_Controller extends WC_REST_Products_V2_Controller {
*/
protected $namespace = 'wc/v3';
private $search_param = 'search';
private static $wp_query_search_param = 's';
/**
* Get the images for a product or product variation.
*
@ -70,6 +73,66 @@ class WC_REST_Products_Controller extends WC_REST_Products_V2_Controller {
return $images;
}
/**
* Get a collection of products and add other search criteria to WP_Query.
*
* @param WP_REST_Request $request Full details about the request.
* @return WP_Error|WP_REST_Response
*/
public function get_items( $request ) {
// Add filters for search criteria in product postmeta via the lookup table.
if ( ! empty( $request[ $this->search_param ] ) ) {
add_filter( 'posts_where', array( __CLASS__, 'add_search_criteria_to_wp_query_filter' ), 10, 2 );
add_filter( 'posts_join', array( __CLASS__, 'add_search_criteria_to_wp_query_join' ), 10, 2 );
}
$response = parent::get_items( $request );
// Remove filters for search criteria in product postmeta via the lookup table.
if ( ! empty( $request[ $this->search_param ] ) ) {
remove_filter( 'posts_where', array( __CLASS__, 'add_search_criteria_to_wp_query_filter' ), 10 );
remove_filter( 'posts_join', array( __CLASS__, 'add_search_criteria_to_wp_query_join' ), 10 );
}
return $response;
}
/**
* Add in conditional search filters for products.
*
* @param string $where Where clause used to search posts.
* @param object $wp_query WP_Query object.
* @return string
*/
public static function add_search_criteria_to_wp_query_filter( $where, $wp_query ) {
global $wpdb;
$search = $wp_query->get( self::$wp_query_search_param );
if ( ! empty( $search ) ) {
$like_search = '%' . $wpdb->esc_like( $search ) . '%';
$conditions = array(
$wpdb->prepare( 'wc_product_meta_lookup.sku LIKE %s', $like_search ),
);
$where .= ' OR (' . implode( ' OR ', $conditions ) . ')';
}
return $where;
}
/**
* Join `wc_product_meta_lookup` table when product search query is present.
*
* @param string $join Join clause used to search posts.
* @param object $wp_query WP_Query object.
* @return string
*/
public static function add_search_criteria_to_wp_query_join( $join, $wp_query ) {
global $wpdb;
$search = $wp_query->get( self::$wp_query_search_param );
if ( ! empty( $search ) ) {
$join .= " LEFT JOIN {$wpdb->wc_product_meta_lookup} wc_product_meta_lookup
ON $wpdb->posts.ID = wc_product_meta_lookup.product_id ";
}
return $join;
}
/**
* Make extra product orderby features supported by WooCommerce available to the WC API.
* This includes 'price', 'popularity', and 'rating'.

View File

@ -147,4 +147,68 @@ class WC_REST_Products_Controller_Tests extends WC_REST_Unit_Test_Case {
$this->assertContains( $field, $response_fields, "Field $field was expected but not present in product API response." );
}
}
/**
* Test that products with partial name or SKU case-insensitive match are returned given a `search` parameter.
*/
public function test_products_with_search_param_returns_products_with_sku_and_name_match() {
// Given.
wp_set_current_user( $this->user );
WC_Helper_Product::create_simple_product( true, array( 'name' => 'WooCommerce Tests' ) );
WC_Helper_Product::create_simple_product( true, array( 'name' => 'WordCamp' ) );
WC_Helper_Product::create_simple_product( true, array( 'name' => 'WoO' ) );
WC_Helper_Product::create_simple_product( true, array( 'sku' => 'Woo' ) );
WC_Helper_Product::create_simple_product( true, array( 'sku' => 'wordpress' ) );
WC_Helper_Product::create_simple_product( true, array( 'sku' => '*sunglasses-woo*' ) );
// When.
$request = new WP_REST_Request( 'GET', '/wc/v3/products' );
$request->set_query_params(
array(
'search' => 'woo',
'order' => 'asc',
'orderby' => 'id',
)
);
$response = $this->server->dispatch( $request );
$this->assertEquals( 200, $response->get_status() );
$response_products = $response->get_data();
// Then.
$this->assertEquals( 4, count( $response_products ) );
$this->assertEquals( $response_products[0]['name'], 'WooCommerce Tests' );
$this->assertEquals( $response_products[1]['name'], 'WoO' );
$this->assertEquals( $response_products[2]['sku'], 'Woo' );
$this->assertEquals( $response_products[3]['sku'], '*sunglasses-woo*' );
}
/**
* Test that the first product with full SKU case-insensitive match are returned given a `sku` parameter.
*/
public function test_products_with_sku_param_returns_the_first_product_with_full_sku_match() {
// Given.
wp_set_current_user( $this->user );
WC_Helper_Product::create_simple_product( true, array( 'sku' => '0oWoO' ) );
WC_Helper_Product::create_simple_product( true, array( 'sku' => 'WoO' ) );
WC_Helper_Product::create_simple_product( true, array( 'sku' => 'woo' ) );
WC_Helper_Product::create_simple_product( true, array( 'sku' => 'woo-sunglasses' ) );
// When.
$request = new WP_REST_Request( 'GET', '/wc/v3/products' );
$request->set_query_params(
array(
'sku' => 'woo',
'order' => 'asc',
'orderby' => 'id',
)
);
$response = $this->server->dispatch( $request );
$this->assertEquals( 200, $response->get_status() );
$response_products = $response->get_data();
// Then.
$this->assertEquals( 1, count( $response_products ) );
$response_product = $response_products[0];
$this->assertEquals( 'WoO', $response_product['sku'] );
}
}