REST API: Update products endpoint to get products across different attributes (https://github.com/woocommerce/woocommerce-blocks/pull/299)
* Add products by attributes tests * Add new properties to Products endpoint args Allows for requesting a combination of attribute terms across different attributes. * Unskip working tests
This commit is contained in:
parent
9e89e32213
commit
1977e1586b
|
@ -230,9 +230,11 @@ class WGPB_Products_Controller extends WC_REST_Products_Controller {
|
||||||
protected function prepare_objects_query( $request ) {
|
protected function prepare_objects_query( $request ) {
|
||||||
$args = parent::prepare_objects_query( $request );
|
$args = parent::prepare_objects_query( $request );
|
||||||
|
|
||||||
$orderby = $request->get_param( 'orderby' );
|
$orderby = $request->get_param( 'orderby' );
|
||||||
$order = $request->get_param( 'order' );
|
$order = $request->get_param( 'order' );
|
||||||
$cat_operator = $request->get_param( 'cat_operator' );
|
$cat_operator = $request->get_param( 'cat_operator' );
|
||||||
|
$attributes = $request->get_param( 'attributes' );
|
||||||
|
$attr_operator = $request->get_param( 'attr_operator' );
|
||||||
|
|
||||||
$ordering_args = WC()->query->get_catalog_ordering_args( $orderby, $order );
|
$ordering_args = WC()->query->get_catalog_ordering_args( $orderby, $order );
|
||||||
$args['orderby'] = $ordering_args['orderby'];
|
$args['orderby'] = $ordering_args['orderby'];
|
||||||
|
@ -250,6 +252,30 @@ class WGPB_Products_Controller extends WC_REST_Products_Controller {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$tax_query = array();
|
||||||
|
if ( $attributes ) {
|
||||||
|
foreach ( $attributes as $attribute => $attribute_terms ) {
|
||||||
|
if ( in_array( $attribute, wc_get_attribute_taxonomy_names(), true ) ) {
|
||||||
|
$tax_query[] = array(
|
||||||
|
'taxonomy' => $attribute,
|
||||||
|
'field' => 'term_id',
|
||||||
|
'terms' => $attribute_terms,
|
||||||
|
'operator' => ! $attr_operator ? 'IN' : $attr_operator,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Merge attribute `$tax_query`s into the request's WP_Query args.
|
||||||
|
if ( ! empty( $tax_query ) ) {
|
||||||
|
if ( ! empty( $args['tax_query'] ) ) {
|
||||||
|
$args['tax_query'] = array_merge( $tax_query, $args['tax_query'] ); // WPCS: slow query ok.
|
||||||
|
} else {
|
||||||
|
$args['tax_query'] = $tax_query; // WPCS: slow query ok.
|
||||||
|
}
|
||||||
|
$args['tax_query']['relation'] = 'AND' === $attr_operator ? 'AND' : 'OR';
|
||||||
|
}
|
||||||
|
|
||||||
return $args;
|
return $args;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -292,6 +318,31 @@ class WGPB_Products_Controller extends WC_REST_Products_Controller {
|
||||||
'sanitize_callback' => 'sanitize_text_field',
|
'sanitize_callback' => 'sanitize_text_field',
|
||||||
'validate_callback' => 'rest_validate_request_arg',
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
);
|
);
|
||||||
|
$params['attr_operator'] = array(
|
||||||
|
'description' => __( 'Operator to compare product attribute terms.', 'woo-gutenberg-products-block' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'enum' => array( 'IN', 'AND' ),
|
||||||
|
'sanitize_callback' => 'sanitize_text_field',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
|
||||||
|
$attr_properties = array();
|
||||||
|
foreach ( wc_get_attribute_taxonomy_names() as $name ) {
|
||||||
|
$attr_properties[ $name ] = array(
|
||||||
|
'type' => 'array',
|
||||||
|
'items' => array( 'type' => 'string' ),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$params['attributes'] = array(
|
||||||
|
'description' => __( 'Map of attributes to selected terms.', 'woo-gutenberg-products-block' ),
|
||||||
|
'type' => 'object',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
if ( ! empty( $attr_properties ) ) {
|
||||||
|
$params['attributes']['properties'] = $attr_properties;
|
||||||
|
$params['attributes']['additionalProperties'] = false;
|
||||||
|
}
|
||||||
|
|
||||||
return $params;
|
return $params;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,211 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @package WooCommerce\Tests\API
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Product Controller "products by attributes" REST API Test
|
||||||
|
*
|
||||||
|
* @since 1.2.0
|
||||||
|
*/
|
||||||
|
class WC_Tests_API_Products_By_Attributes_Controller extends WC_REST_Unit_Test_Case {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Endpoints.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $endpoint = '/wc-pb/v3';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setup test products data. Called before every test.
|
||||||
|
*
|
||||||
|
* @since 1.2.0
|
||||||
|
*/
|
||||||
|
public function setUp() {
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
$this->user = $this->factory->user->create(
|
||||||
|
array(
|
||||||
|
'role' => 'administrator',
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Create 2 product attributes with terms.
|
||||||
|
$attr_color = WC_Helper_Product::create_attribute( 'color', array( 'red', 'yellow', 'blue' ) );
|
||||||
|
$attr_size = WC_Helper_Product::create_attribute( 'size', array( 'small', 'medium', 'large' ) );
|
||||||
|
|
||||||
|
$red_term = get_term_by( 'slug', 'red', $attr_color['attribute_taxonomy'] );
|
||||||
|
$blue_term = get_term_by( 'slug', 'blue', $attr_color['attribute_taxonomy'] );
|
||||||
|
$yellow_term = get_term_by( 'slug', 'yellow', $attr_color['attribute_taxonomy'] );
|
||||||
|
$small_term = get_term_by( 'slug', 'small', $attr_size['attribute_taxonomy'] );
|
||||||
|
$medium_term = get_term_by( 'slug', 'medium', $attr_size['attribute_taxonomy'] );
|
||||||
|
$large_term = get_term_by( 'slug', 'large', $attr_size['attribute_taxonomy'] );
|
||||||
|
|
||||||
|
$this->attr_term_ids = array(
|
||||||
|
'red' => $red_term->term_id,
|
||||||
|
'blue' => $blue_term->term_id,
|
||||||
|
'yellow' => $yellow_term->term_id,
|
||||||
|
'small' => $small_term->term_id,
|
||||||
|
'medium' => $medium_term->term_id,
|
||||||
|
'large' => $large_term->term_id,
|
||||||
|
);
|
||||||
|
|
||||||
|
$color = new WC_Product_Attribute();
|
||||||
|
$color->set_id( $attr_color['attribute_id'] );
|
||||||
|
$color->set_name( $attr_color['attribute_taxonomy'] );
|
||||||
|
$color->set_visible( true );
|
||||||
|
|
||||||
|
$size = new WC_Product_Attribute();
|
||||||
|
$size->set_id( $attr_size['attribute_id'] );
|
||||||
|
$size->set_name( $attr_size['attribute_taxonomy'] );
|
||||||
|
$size->set_visible( true );
|
||||||
|
|
||||||
|
// Create some products with a mix of attributes:
|
||||||
|
// - Product 1 – color: red, blue; size: medium.
|
||||||
|
// - Product 2 – color: blue; size: large, medium.
|
||||||
|
// - Product 3 – color: yellow.
|
||||||
|
$this->products = array();
|
||||||
|
$color->set_options( [ $this->attr_term_ids['red'], $this->attr_term_ids['blue'] ] );
|
||||||
|
$size->set_options( [ $this->attr_term_ids['medium'] ] );
|
||||||
|
$this->products[0] = WC_Helper_Product::create_simple_product( false );
|
||||||
|
$this->products[0]->set_attributes( [ $color, $size ] );
|
||||||
|
$this->products[0]->save();
|
||||||
|
|
||||||
|
$color->set_options( [ $this->attr_term_ids['blue'] ] );
|
||||||
|
$size->set_options( [ $this->attr_term_ids['medium'], $this->attr_term_ids['large'] ] );
|
||||||
|
$this->products[1] = WC_Helper_Product::create_simple_product( false );
|
||||||
|
$this->products[1]->set_attributes( [ $color, $size ] );
|
||||||
|
$this->products[1]->save();
|
||||||
|
|
||||||
|
$color->set_options( [ $this->attr_term_ids['yellow'] ] );
|
||||||
|
$this->products[2] = WC_Helper_Product::create_simple_product( false );
|
||||||
|
$this->products[2]->set_attributes( [ $color ] );
|
||||||
|
$this->products[2]->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test getting products by a single attribute term.
|
||||||
|
*
|
||||||
|
* @since 1.2.0
|
||||||
|
*/
|
||||||
|
public function test_get_products() {
|
||||||
|
wp_set_current_user( $this->user );
|
||||||
|
$request = new WP_REST_Request( 'GET', $this->endpoint . '/products' );
|
||||||
|
$request->set_param( 'attribute', 'pa_color' );
|
||||||
|
$request->set_param( 'attribute_term', (string) $this->attr_term_ids['red'] );
|
||||||
|
|
||||||
|
$response = $this->server->dispatch( $request );
|
||||||
|
$response_products = $response->get_data();
|
||||||
|
$this->assertEquals( 200, $response->get_status() );
|
||||||
|
$this->assertEquals( 1, count( $response_products ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test getting products by multiple terms in one attribute.
|
||||||
|
*
|
||||||
|
* @since 1.2.0
|
||||||
|
*/
|
||||||
|
public function test_get_products_by_multiple_terms() {
|
||||||
|
wp_set_current_user( $this->user );
|
||||||
|
$request = new WP_REST_Request( 'GET', $this->endpoint . '/products' );
|
||||||
|
$request->set_param( 'attribute', 'pa_color' );
|
||||||
|
$request->set_param(
|
||||||
|
'attribute_term',
|
||||||
|
// Terms list needs to be a string.
|
||||||
|
$this->attr_term_ids['red'] . ',' . $this->attr_term_ids['yellow']
|
||||||
|
);
|
||||||
|
|
||||||
|
$response = $this->server->dispatch( $request );
|
||||||
|
$response_products = $response->get_data();
|
||||||
|
$this->assertEquals( 200, $response->get_status() );
|
||||||
|
$this->assertEquals( 2, count( $response_products ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test getting products by multiple terms in one attribute, matching all.
|
||||||
|
*
|
||||||
|
* @since 1.2.0
|
||||||
|
*/
|
||||||
|
public function test_get_products_by_multiple_terms_all() {
|
||||||
|
wp_set_current_user( $this->user );
|
||||||
|
$request = new WP_REST_Request( 'GET', $this->endpoint . '/products' );
|
||||||
|
$request->set_param(
|
||||||
|
'attributes',
|
||||||
|
array(
|
||||||
|
'pa_color' => array(
|
||||||
|
$this->attr_term_ids['red'],
|
||||||
|
$this->attr_term_ids['blue'],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
$request->set_param( 'attr_operator', 'AND' );
|
||||||
|
|
||||||
|
$response = $this->server->dispatch( $request );
|
||||||
|
$response_products = $response->get_data();
|
||||||
|
$this->assertEquals( 200, $response->get_status() );
|
||||||
|
$this->assertEquals( 1, count( $response_products ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test getting products by multiple terms in multiple attributes, matching any.
|
||||||
|
*
|
||||||
|
* @since 1.2.0
|
||||||
|
*/
|
||||||
|
public function test_get_products_by_multiple_terms_multiple_attrs_any() {
|
||||||
|
wp_set_current_user( $this->user );
|
||||||
|
$request = new WP_REST_Request( 'GET', $this->endpoint . '/products' );
|
||||||
|
$request->set_param(
|
||||||
|
'attributes',
|
||||||
|
array(
|
||||||
|
'pa_color' => array( $this->attr_term_ids['red'] ),
|
||||||
|
'pa_size' => array( $this->attr_term_ids['large'] ),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
$request->set_param( 'attr_operator', 'IN' );
|
||||||
|
|
||||||
|
$response = $this->server->dispatch( $request );
|
||||||
|
$response_products = $response->get_data();
|
||||||
|
$this->assertEquals( 200, $response->get_status() );
|
||||||
|
$this->assertEquals( 2, count( $response_products ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test getting products by multiple terms in multiple attributes, matching all.
|
||||||
|
*
|
||||||
|
* @since 1.2.0
|
||||||
|
*/
|
||||||
|
public function test_get_products_by_multiple_terms_multiple_attrs_all() {
|
||||||
|
wp_set_current_user( $this->user );
|
||||||
|
$request = new WP_REST_Request( 'GET', $this->endpoint . '/products' );
|
||||||
|
$request->set_param(
|
||||||
|
'attributes',
|
||||||
|
array(
|
||||||
|
'pa_color' => array( $this->attr_term_ids['blue'] ),
|
||||||
|
'pa_size' => array( $this->attr_term_ids['medium'] ),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
$request->set_param( 'attr_operator', 'AND' );
|
||||||
|
|
||||||
|
$response = $this->server->dispatch( $request );
|
||||||
|
$response_products = $response->get_data();
|
||||||
|
$this->assertEquals( 200, $response->get_status() );
|
||||||
|
$this->assertEquals( 2, count( $response_products ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test getting products by attributes that don't exist.
|
||||||
|
*
|
||||||
|
* Note: This test is currently skipped because the API isn't registering the attribute
|
||||||
|
* properties correctly, and therefor not validating attribute names against "real" attributes.
|
||||||
|
*
|
||||||
|
* @since 1.2.0
|
||||||
|
*/
|
||||||
|
public function xtest_get_products_by_fake_attrs() {
|
||||||
|
wp_set_current_user( $this->user );
|
||||||
|
$request = new WP_REST_Request( 'GET', $this->endpoint . '/products' );
|
||||||
|
$request->set_param( 'attributes', array( 'pa_fake' => array( 21 ) ) );
|
||||||
|
$response = $this->server->dispatch( $request );
|
||||||
|
$this->assertEquals( 400, $response->get_status() );
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue