From 4aa16890151b0e99a8eae51d5998ad0844695cbf Mon Sep 17 00:00:00 2001 From: Claudio Sanches Date: Mon, 10 Oct 2016 22:12:03 -0300 Subject: [PATCH 01/12] Better description for product taxonomy filters --- includes/api/class-wc-rest-products-controller.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/includes/api/class-wc-rest-products-controller.php b/includes/api/class-wc-rest-products-controller.php index 3a23e960ee9..964632902be 100644 --- a/includes/api/class-wc-rest-products-controller.php +++ b/includes/api/class-wc-rest-products-controller.php @@ -2690,19 +2690,19 @@ class WC_REST_Products_Controller extends WC_REST_Posts_Controller { 'validate_callback' => 'rest_validate_request_arg', ); $params['category'] = array( - 'description' => __( 'Limit result set to products assigned a specific category.', 'woocommerce' ), + 'description' => __( 'Limit result set to products assigned a specific category ID.', 'woocommerce' ), 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field', 'validate_callback' => 'rest_validate_request_arg', ); $params['tag'] = array( - 'description' => __( 'Limit result set to products assigned a specific tag.', 'woocommerce' ), + 'description' => __( 'Limit result set to products assigned a specific tag ID.', 'woocommerce' ), 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field', 'validate_callback' => 'rest_validate_request_arg', ); $params['shipping_class'] = array( - 'description' => __( 'Limit result set to products assigned a specific shipping class.', 'woocommerce' ), + 'description' => __( 'Limit result set to products assigned a specific shipping class ID.', 'woocommerce' ), 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field', 'validate_callback' => 'rest_validate_request_arg', @@ -2714,7 +2714,7 @@ class WC_REST_Products_Controller extends WC_REST_Posts_Controller { 'validate_callback' => 'rest_validate_request_arg', ); $params['attribute_term'] = array( - 'description' => __( 'Limit result set to products with a specific attribute term (required an assigned attribute).', 'woocommerce' ), + 'description' => __( 'Limit result set to products with a specific attribute term ID (required an assigned attribute).', 'woocommerce' ), 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field', 'validate_callback' => 'rest_validate_request_arg', From 876f7e62d7da8c564e9422c87f60417eade50b47 Mon Sep 17 00:00:00 2001 From: Claudio Sanches Date: Mon, 10 Oct 2016 22:56:41 -0300 Subject: [PATCH 02/12] Updated WP REST API functions to 2.0-beta15 --- includes/vendor/wp-rest-functions.php | 155 ++++++++++++++++++++++---- 1 file changed, 136 insertions(+), 19 deletions(-) diff --git a/includes/vendor/wp-rest-functions.php b/includes/vendor/wp-rest-functions.php index 83d2dee259a..636673f5bc2 100644 --- a/includes/vendor/wp-rest-functions.php +++ b/includes/vendor/wp-rest-functions.php @@ -1,6 +1,6 @@ = $args['maximum'] ) { - return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s must be less than %2$d (exclusive)', 'woocommerce' ), $param, $args['maximum'] ) ); - } elseif ( empty( $args['exclusiveMaximum'] ) && $value > $args['maximum'] ) { - return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s must be less than %2$d (inclusive)', 'woocommerce' ), $param, $args['maximum'] ) ); + return new WP_Error( 'rest_invalid_param', sprintf( __( '%s must be less than %d (exclusive)', 'woocommerce' ), $param, $args['maximum'] ) ); + } else if ( empty( $args['exclusiveMaximum'] ) && $value > $args['maximum'] ) { + return new WP_Error( 'rest_invalid_param', sprintf( __( '%s must be less than %d (inclusive)', 'woocommerce' ), $param, $args['maximum'] ) ); } - } elseif ( isset( $args['maximum'] ) && isset( $args['minimum'] ) ) { + } else if ( isset( $args['maximum'] ) && isset( $args['minimum'] ) ) { if ( ! empty( $args['exclusiveMinimum'] ) && ! empty( $args['exclusiveMaximum'] ) ) { if ( $value >= $args['maximum'] || $value <= $args['minimum'] ) { - return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s must be between %2$d (exclusive) and %3$d (exclusive)', 'woocommerce' ), $param, $args['minimum'], $args['maximum'] ) ); + return new WP_Error( 'rest_invalid_param', sprintf( __( '%s must be between %d (exclusive) and %d (exclusive)', 'woocommerce' ), $param, $args['minimum'], $args['maximum'] ) ); } - } elseif ( empty( $args['exclusiveMinimum'] ) && ! empty( $args['exclusiveMaximum'] ) ) { + } else if ( empty( $args['exclusiveMinimum'] ) && ! empty( $args['exclusiveMaximum'] ) ) { if ( $value >= $args['maximum'] || $value < $args['minimum'] ) { - return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s must be between %2$d (inclusive) and %3$d (exclusive)', 'woocommerce' ), $param, $args['minimum'], $args['maximum'] ) ); + return new WP_Error( 'rest_invalid_param', sprintf( __( '%s must be between %d (inclusive) and %d (exclusive)', 'woocommerce' ), $param, $args['minimum'], $args['maximum'] ) ); } - } elseif ( ! empty( $args['exclusiveMinimum'] ) && empty( $args['exclusiveMaximum'] ) ) { + } else if ( ! empty( $args['exclusiveMinimum'] ) && empty( $args['exclusiveMaximum'] ) ) { if ( $value > $args['maximum'] || $value <= $args['minimum'] ) { - return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s must be between %2$d (exclusive) and %3$d (inclusive)', 'woocommerce' ), $param, $args['minimum'], $args['maximum'] ) ); + return new WP_Error( 'rest_invalid_param', sprintf( __( '%s must be between %d (exclusive) and %d (inclusive)', 'woocommerce' ), $param, $args['minimum'], $args['maximum'] ) ); } - } elseif ( empty( $args['exclusiveMinimum'] ) && empty( $args['exclusiveMaximum'] ) ) { + } else if ( empty( $args['exclusiveMinimum'] ) && empty( $args['exclusiveMaximum'] ) ) { if ( $value > $args['maximum'] || $value < $args['minimum'] ) { - return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s must be between %2$d (inclusive) and %3$d (inclusive)', 'woocommerce' ), $param, $args['minimum'], $args['maximum'] ) ); + return new WP_Error( 'rest_invalid_param', sprintf( __( '%s must be between %d (inclusive) and %d (inclusive)', 'woocommerce' ), $param, $args['minimum'], $args['maximum'] ) ); } } } @@ -253,6 +262,10 @@ if ( ! function_exists( 'rest_sanitize_request_arg' ) ) { return (int) $value; } + if ( 'boolean' === $args['type'] ) { + return rest_sanitize_boolean( $value ); + } + if ( isset( $args['format'] ) ) { switch ( $args['format'] ) { case 'date-time' : @@ -266,9 +279,113 @@ if ( ! function_exists( 'rest_sanitize_request_arg' ) ) { case 'uri' : return esc_url_raw( $value ); + + case 'ipv4' : + return sanitize_text_field( $value ); } } return $value; } } + + +if ( ! function_exists( 'rest_parse_request_arg' ) ) { + /** + * Parse a request argument based on details registered to the route. + * + * Runs a validation check and sanitizes the value, primarily to be used via + * the `sanitize_callback` arguments in the endpoint args registration. + * + * @param mixed $value + * @param WP_REST_Request $request + * @param string $param + * @return mixed + */ + function rest_parse_request_arg( $value, $request, $param ) { + + $is_valid = rest_validate_request_arg( $value, $request, $param ); + + if ( is_wp_error( $is_valid ) ) { + return $is_valid; + } + + $value = rest_sanitize_request_arg( $value, $request, $param ); + + return $value; + } +} + +if ( ! function_exists( 'rest_is_ip_address' ) ) { + /** + * Determines if a IPv4 address is valid. + * + * Does not handle IPv6 addresses. + * + * @param string $ipv4 IP 32-bit address. + * @return string|false The valid IPv4 address, otherwise false. + */ + function rest_is_ip_address( $ipv4 ) { + $pattern = '/^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/'; + + if ( ! preg_match( $pattern, $ipv4 ) ) { + return false; + } + + return $ipv4; + } +} + +/** + * Changes a boolean-like value into the proper boolean value. + * + * @param bool|string|int $value The value being evaluated. + * @return boolean Returns the proper associated boolean value. + */ +if ( ! function_exists( 'rest_sanitize_boolean' ) ) { + function rest_sanitize_boolean( $value ) { + // String values are translated to `true`; make sure 'false' is false. + if ( is_string( $value ) ) { + $value = strtolower( $value ); + if ( in_array( $value, array( 'false', '0' ), true ) ) { + $value = false; + } + } + + // Everything else will map nicely to boolean. + return (boolean) $value; + } +} + +/** + * Determines if a given value is boolean-like. + * + * @param bool|string $maybe_bool The value being evaluated. + * @return boolean True if a boolean, otherwise false. + */ +if ( ! function_exists( 'rest_is_boolean' ) ) { + function rest_is_boolean( $maybe_bool ) { + if ( is_bool( $maybe_bool ) ) { + return true; + } + + if ( is_string( $maybe_bool ) ) { + $maybe_bool = strtolower( $maybe_bool ); + + $valid_boolean_values = array( + 'false', + 'true', + '0', + '1', + ); + + return in_array( $maybe_bool, $valid_boolean_values, true ); + } + + if ( is_int( $maybe_bool ) ) { + return in_array( $maybe_bool, array( 0, 1 ), true ); + } + + return false; + } +} From 92404fc867e3dc753189aeeeb0497316680dda23 Mon Sep 17 00:00:00 2001 From: Claudio Sanches Date: Mon, 10 Oct 2016 22:57:04 -0300 Subject: [PATCH 03/12] Allow filter products by featured post meta --- .../api/class-wc-rest-products-controller.php | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/includes/api/class-wc-rest-products-controller.php b/includes/api/class-wc-rest-products-controller.php index 964632902be..f587e675478 100644 --- a/includes/api/class-wc-rest-products-controller.php +++ b/includes/api/class-wc-rest-products-controller.php @@ -197,6 +197,17 @@ class WC_REST_Products_Controller extends WC_REST_Posts_Controller { ); } + if ( is_bool( $request['featured'] ) ) { + if ( ! empty( $args['meta_query'] ) ) { + $args['meta_query'] = array(); + } + + $args['meta_query'][] = array( + 'key' => '_featured', + 'value' => true === $request['featured'] ? 'yes' : 'no', + ); + } + // Apply all WP_Query filters again. if ( is_array( $request['filter'] ) ) { $args = array_merge( $args, $request['filter'] ); @@ -2725,6 +2736,12 @@ class WC_REST_Products_Controller extends WC_REST_Posts_Controller { 'sanitize_callback' => 'sanitize_text_field', 'validate_callback' => 'rest_validate_request_arg', ); + $params['featured'] = array( + 'description' => __( 'Limit result set to featured products.', 'woocommerce' ), + 'type' => 'boolean', + 'sanitize_callback' => 'wp_validate_boolean', + 'validate_callback' => 'rest_validate_request_arg', + ); return $params; } From 842c39b5250e01aac5b44d90ee4b40d57648a730 Mon Sep 17 00:00:00 2001 From: Claudio Sanches Date: Mon, 10 Oct 2016 23:09:47 -0300 Subject: [PATCH 04/12] Make easy to add query_meta --- .../abstracts/abstract-wc-rest-controller.php | 18 ++++++++ .../api/class-wc-rest-products-controller.php | 45 ++++++++----------- 2 files changed, 37 insertions(+), 26 deletions(-) diff --git a/includes/abstracts/abstract-wc-rest-controller.php b/includes/abstracts/abstract-wc-rest-controller.php index 8bd1dd86cc8..6d4616868e1 100644 --- a/includes/abstracts/abstract-wc-rest-controller.php +++ b/includes/abstracts/abstract-wc-rest-controller.php @@ -319,6 +319,24 @@ abstract class WC_REST_Controller extends WP_REST_Controller { ); } + /** + * Add meta query. + * + * @since 2.7.0 + * @param array $args Query args. + * @param array $meta_query Meta query. + * @return array + */ + protected function add_meta_query( $args, $meta_query ) { + if ( ! empty( $args['meta_query'] ) ) { + $args['meta_query'] = array(); + } + + $args['meta_query'][] = $meta_query; + + return $args['meta_query']; + } + /** * Get the batch schema, conforming to JSON Schema. * diff --git a/includes/api/class-wc-rest-products-controller.php b/includes/api/class-wc-rest-products-controller.php index f587e675478..3eac170ca71 100644 --- a/includes/api/class-wc-rest-products-controller.php +++ b/includes/api/class-wc-rest-products-controller.php @@ -186,26 +186,19 @@ class WC_REST_Products_Controller extends WC_REST_Posts_Controller { // Filter by sku. if ( ! empty( $request['sku'] ) ) { - if ( ! empty( $args['meta_query'] ) ) { - $args['meta_query'] = array(); - } - - $args['meta_query'][] = array( + $args['meta_query'] = $this->add_meta_query( $args, array( 'key' => '_sku', 'value' => $request['sku'], 'compare' => '=', - ); + ) ); } + // Filter featured. if ( is_bool( $request['featured'] ) ) { - if ( ! empty( $args['meta_query'] ) ) { - $args['meta_query'] = array(); - } - - $args['meta_query'][] = array( - 'key' => '_featured', - 'value' => true === $request['featured'] ? 'yes' : 'no', - ); + $args['meta_query'] = $this->add_meta_query( $args, array( + 'key' => '_featured', + 'value' => true === $request['featured'] ? 'yes' : 'no', + ) ); } // Apply all WP_Query filters again. @@ -2700,6 +2693,18 @@ class WC_REST_Products_Controller extends WC_REST_Posts_Controller { 'sanitize_callback' => 'sanitize_key', 'validate_callback' => 'rest_validate_request_arg', ); + $params['sku'] = array( + 'description' => __( 'Limit result set to products with a specific SKU.', 'woocommerce' ), + 'type' => 'string', + 'sanitize_callback' => 'sanitize_text_field', + 'validate_callback' => 'rest_validate_request_arg', + ); + $params['featured'] = array( + 'description' => __( 'Limit result set to featured products.', 'woocommerce' ), + 'type' => 'boolean', + 'sanitize_callback' => 'wp_validate_boolean', + 'validate_callback' => 'rest_validate_request_arg', + ); $params['category'] = array( 'description' => __( 'Limit result set to products assigned a specific category ID.', 'woocommerce' ), 'type' => 'string', @@ -2730,18 +2735,6 @@ class WC_REST_Products_Controller extends WC_REST_Posts_Controller { 'sanitize_callback' => 'sanitize_text_field', 'validate_callback' => 'rest_validate_request_arg', ); - $params['sku'] = array( - 'description' => __( 'Limit result set to products with a specific SKU.', 'woocommerce' ), - 'type' => 'string', - 'sanitize_callback' => 'sanitize_text_field', - 'validate_callback' => 'rest_validate_request_arg', - ); - $params['featured'] = array( - 'description' => __( 'Limit result set to featured products.', 'woocommerce' ), - 'type' => 'boolean', - 'sanitize_callback' => 'wp_validate_boolean', - 'validate_callback' => 'rest_validate_request_arg', - ); return $params; } From e57ae3da2753123bb175a5a4b8067da75b571985 Mon Sep 17 00:00:00 2001 From: Claudio Sanches Date: Mon, 10 Oct 2016 23:18:56 -0300 Subject: [PATCH 05/12] Filter products by tax class --- .../api/class-wc-rest-products-controller.php | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/includes/api/class-wc-rest-products-controller.php b/includes/api/class-wc-rest-products-controller.php index 3eac170ca71..2b1f7cc1d15 100644 --- a/includes/api/class-wc-rest-products-controller.php +++ b/includes/api/class-wc-rest-products-controller.php @@ -201,6 +201,15 @@ class WC_REST_Products_Controller extends WC_REST_Posts_Controller { ) ); } + // Filter by tax class. + if ( ! empty( $request['tax_class'] ) ) { + $args['meta_query'] = $this->add_meta_query( $args, array( + 'key' => '_tax_class', + 'value' => 'standard' !== $request['tax_class'] ? $request['tax_class'] : '', + 'compare' => '=', + ) ); + } + // Apply all WP_Query filters again. if ( is_array( $request['filter'] ) ) { $args = array_merge( $args, $request['filter'] ); @@ -2736,6 +2745,16 @@ class WC_REST_Products_Controller extends WC_REST_Posts_Controller { 'validate_callback' => 'rest_validate_request_arg', ); + if ( wc_tax_enabled() ) { + $params['tax_class'] = array( + 'description' => __( 'Limit result set to products with a specific tax class.', 'woocommerce' ), + 'type' => 'string', + 'enum' => array_map( 'sanitize_title', array_merge( array( 'standard' ), WC_Tax::get_tax_classes() ) ), + 'sanitize_callback' => 'sanitize_text_field', + 'validate_callback' => 'rest_validate_request_arg', + ); + } + return $params; } } From d8a03a30830256e4ba7497fce5cb09eea25a9582 Mon Sep 17 00:00:00 2001 From: Claudio Sanches Date: Mon, 10 Oct 2016 23:21:06 -0300 Subject: [PATCH 06/12] Fixed coding standards --- includes/vendor/wp-rest-functions.php | 38 +++++++++++++-------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/includes/vendor/wp-rest-functions.php b/includes/vendor/wp-rest-functions.php index 636673f5bc2..7b1be71113d 100644 --- a/includes/vendor/wp-rest-functions.php +++ b/includes/vendor/wp-rest-functions.php @@ -166,20 +166,20 @@ if ( ! function_exists( 'rest_validate_request_arg' ) ) { if ( ! empty( $args['enum'] ) ) { if ( ! in_array( $value, $args['enum'] ) ) { - return new WP_Error( 'rest_invalid_param', sprintf( __( '%s is not one of %s', 'woocommerce' ), $param, implode( ', ', $args['enum'] ) ) ); + return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s is not one of %1$s', 'woocommerce' ), $param, implode( ', ', $args['enum'] ) ) ); } } if ( 'integer' === $args['type'] && ! is_numeric( $value ) ) { - return new WP_Error( 'rest_invalid_param', sprintf( __( '%s is not of type %s', 'woocommerce' ), $param, 'integer' ) ); + return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s is not of type %1$s', 'woocommerce' ), $param, 'integer' ) ); } if ( 'boolean' === $args['type'] && ! rest_is_boolean( $value ) ) { - return new WP_Error( 'rest_invalid_param', sprintf( __( '%s is not of type %s', 'woocommerce' ), $value, 'boolean' ) ); + return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s is not of type %1$s', 'woocommerce' ), $value, 'boolean' ) ); } if ( 'string' === $args['type'] && ! is_string( $value ) ) { - return new WP_Error( 'rest_invalid_param', sprintf( __( '%s is not of type %s', 'woocommerce' ), $param, 'string' ) ); + return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s is not of type %1$s', 'woocommerce' ), $param, 'string' ) ); } if ( isset( $args['format'] ) ) { @@ -206,32 +206,32 @@ if ( ! function_exists( 'rest_validate_request_arg' ) ) { if ( in_array( $args['type'], array( 'numeric', 'integer' ) ) && ( isset( $args['minimum'] ) || isset( $args['maximum'] ) ) ) { if ( isset( $args['minimum'] ) && ! isset( $args['maximum'] ) ) { if ( ! empty( $args['exclusiveMinimum'] ) && $value <= $args['minimum'] ) { - return new WP_Error( 'rest_invalid_param', sprintf( __( '%s must be greater than %d (exclusive)', 'woocommerce' ), $param, $args['minimum'] ) ); - } else if ( empty( $args['exclusiveMinimum'] ) && $value < $args['minimum'] ) { - return new WP_Error( 'rest_invalid_param', sprintf( __( '%s must be greater than %d (inclusive)', 'woocommerce' ), $param, $args['minimum'] ) ); + return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s must be greater than %2$d (exclusive)', 'woocommerce' ), $param, $args['minimum'] ) ); + } elseif ( empty( $args['exclusiveMinimum'] ) && $value < $args['minimum'] ) { + return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s must be greater than %2$d (inclusive)', 'woocommerce' ), $param, $args['minimum'] ) ); } - } else if ( isset( $args['maximum'] ) && ! isset( $args['minimum'] ) ) { + } elseif ( isset( $args['maximum'] ) && ! isset( $args['minimum'] ) ) { if ( ! empty( $args['exclusiveMaximum'] ) && $value >= $args['maximum'] ) { - return new WP_Error( 'rest_invalid_param', sprintf( __( '%s must be less than %d (exclusive)', 'woocommerce' ), $param, $args['maximum'] ) ); - } else if ( empty( $args['exclusiveMaximum'] ) && $value > $args['maximum'] ) { - return new WP_Error( 'rest_invalid_param', sprintf( __( '%s must be less than %d (inclusive)', 'woocommerce' ), $param, $args['maximum'] ) ); + return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s must be less than %2$d (exclusive)', 'woocommerce' ), $param, $args['maximum'] ) ); + } elseif ( empty( $args['exclusiveMaximum'] ) && $value > $args['maximum'] ) { + return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s must be less than %2$d (inclusive)', 'woocommerce' ), $param, $args['maximum'] ) ); } - } else if ( isset( $args['maximum'] ) && isset( $args['minimum'] ) ) { + } elseif ( isset( $args['maximum'] ) && isset( $args['minimum'] ) ) { if ( ! empty( $args['exclusiveMinimum'] ) && ! empty( $args['exclusiveMaximum'] ) ) { if ( $value >= $args['maximum'] || $value <= $args['minimum'] ) { - return new WP_Error( 'rest_invalid_param', sprintf( __( '%s must be between %d (exclusive) and %d (exclusive)', 'woocommerce' ), $param, $args['minimum'], $args['maximum'] ) ); + return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s must be between %2$d (exclusive) and %2$d (exclusive)', 'woocommerce' ), $param, $args['minimum'], $args['maximum'] ) ); } - } else if ( empty( $args['exclusiveMinimum'] ) && ! empty( $args['exclusiveMaximum'] ) ) { + } elseif ( empty( $args['exclusiveMinimum'] ) && ! empty( $args['exclusiveMaximum'] ) ) { if ( $value >= $args['maximum'] || $value < $args['minimum'] ) { - return new WP_Error( 'rest_invalid_param', sprintf( __( '%s must be between %d (inclusive) and %d (exclusive)', 'woocommerce' ), $param, $args['minimum'], $args['maximum'] ) ); + return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s must be between %2$d (inclusive) and %2$d (exclusive)', 'woocommerce' ), $param, $args['minimum'], $args['maximum'] ) ); } - } else if ( ! empty( $args['exclusiveMinimum'] ) && empty( $args['exclusiveMaximum'] ) ) { + } elseif ( ! empty( $args['exclusiveMinimum'] ) && empty( $args['exclusiveMaximum'] ) ) { if ( $value > $args['maximum'] || $value <= $args['minimum'] ) { - return new WP_Error( 'rest_invalid_param', sprintf( __( '%s must be between %d (exclusive) and %d (inclusive)', 'woocommerce' ), $param, $args['minimum'], $args['maximum'] ) ); + return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s must be between %2$d (exclusive) and %2$d (inclusive)', 'woocommerce' ), $param, $args['minimum'], $args['maximum'] ) ); } - } else if ( empty( $args['exclusiveMinimum'] ) && empty( $args['exclusiveMaximum'] ) ) { + } elseif ( empty( $args['exclusiveMinimum'] ) && empty( $args['exclusiveMaximum'] ) ) { if ( $value > $args['maximum'] || $value < $args['minimum'] ) { - return new WP_Error( 'rest_invalid_param', sprintf( __( '%s must be between %d (inclusive) and %d (inclusive)', 'woocommerce' ), $param, $args['minimum'], $args['maximum'] ) ); + return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s must be between %2$d (inclusive) and %2$d (inclusive)', 'woocommerce' ), $param, $args['minimum'], $args['maximum'] ) ); } } } From 2400a72b936193efcb2bcdc53e8c574d6f6604a1 Mon Sep 17 00:00:00 2001 From: Claudio Sanches Date: Mon, 10 Oct 2016 23:46:52 -0300 Subject: [PATCH 07/12] Filter products in stock or out of stock --- .../api/class-wc-rest-products-controller.php | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/includes/api/class-wc-rest-products-controller.php b/includes/api/class-wc-rest-products-controller.php index 2b1f7cc1d15..a11b77b2e27 100644 --- a/includes/api/class-wc-rest-products-controller.php +++ b/includes/api/class-wc-rest-products-controller.php @@ -210,6 +210,14 @@ class WC_REST_Products_Controller extends WC_REST_Posts_Controller { ) ); } + // Filter product in stock or out of stock. + if ( is_bool( $request['in_stock'] ) ) { + $args['meta_query'] = $this->add_meta_query( $args, array( + 'key' => '_stock_status', + 'value' => true === $request['in_stock'] ? 'instock' : 'outofstock', + ) ); + } + // Apply all WP_Query filters again. if ( is_array( $request['filter'] ) ) { $args = array_merge( $args, $request['filter'] ); @@ -2711,7 +2719,7 @@ class WC_REST_Products_Controller extends WC_REST_Posts_Controller { $params['featured'] = array( 'description' => __( 'Limit result set to featured products.', 'woocommerce' ), 'type' => 'boolean', - 'sanitize_callback' => 'wp_validate_boolean', + 'sanitize_callback' => 'wc_string_to_bool', 'validate_callback' => 'rest_validate_request_arg', ); $params['category'] = array( @@ -2755,6 +2763,13 @@ class WC_REST_Products_Controller extends WC_REST_Posts_Controller { ); } + $params['in_stock'] = array( + 'description' => __( 'Limit result set to products in stock or out of stock.', 'woocommerce' ), + 'type' => 'boolean', + 'sanitize_callback' => 'wc_string_to_bool', + 'validate_callback' => 'rest_validate_request_arg', + ); + return $params; } } From 861606571d10d438e58e91c97b08d13733d49032 Mon Sep 17 00:00:00 2001 From: Claudio Sanches Date: Mon, 10 Oct 2016 23:57:56 -0300 Subject: [PATCH 08/12] Fixed PHPCBF mistakes --- includes/vendor/wp-rest-functions.php | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/includes/vendor/wp-rest-functions.php b/includes/vendor/wp-rest-functions.php index 7b1be71113d..aec4b332ed3 100644 --- a/includes/vendor/wp-rest-functions.php +++ b/includes/vendor/wp-rest-functions.php @@ -166,20 +166,20 @@ if ( ! function_exists( 'rest_validate_request_arg' ) ) { if ( ! empty( $args['enum'] ) ) { if ( ! in_array( $value, $args['enum'] ) ) { - return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s is not one of %1$s', 'woocommerce' ), $param, implode( ', ', $args['enum'] ) ) ); + return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s is not one of %2$s', 'woocommerce' ), $param, implode( ', ', $args['enum'] ) ) ); } } if ( 'integer' === $args['type'] && ! is_numeric( $value ) ) { - return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s is not of type %1$s', 'woocommerce' ), $param, 'integer' ) ); + return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s is not of type %2$s', 'woocommerce' ), $param, 'integer' ) ); } if ( 'boolean' === $args['type'] && ! rest_is_boolean( $value ) ) { - return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s is not of type %1$s', 'woocommerce' ), $value, 'boolean' ) ); + return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s is not of type %2$s', 'woocommerce' ), $value, 'boolean' ) ); } if ( 'string' === $args['type'] && ! is_string( $value ) ) { - return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s is not of type %1$s', 'woocommerce' ), $param, 'string' ) ); + return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s is not of type %2$s', 'woocommerce' ), $param, 'string' ) ); } if ( isset( $args['format'] ) ) { @@ -219,19 +219,19 @@ if ( ! function_exists( 'rest_validate_request_arg' ) ) { } elseif ( isset( $args['maximum'] ) && isset( $args['minimum'] ) ) { if ( ! empty( $args['exclusiveMinimum'] ) && ! empty( $args['exclusiveMaximum'] ) ) { if ( $value >= $args['maximum'] || $value <= $args['minimum'] ) { - return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s must be between %2$d (exclusive) and %2$d (exclusive)', 'woocommerce' ), $param, $args['minimum'], $args['maximum'] ) ); + return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s must be between %2$d (exclusive) and %3$d (exclusive)', 'woocommerce' ), $param, $args['minimum'], $args['maximum'] ) ); } } elseif ( empty( $args['exclusiveMinimum'] ) && ! empty( $args['exclusiveMaximum'] ) ) { if ( $value >= $args['maximum'] || $value < $args['minimum'] ) { - return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s must be between %2$d (inclusive) and %2$d (exclusive)', 'woocommerce' ), $param, $args['minimum'], $args['maximum'] ) ); + return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s must be between %2$d (inclusive) and %3$d (exclusive)', 'woocommerce' ), $param, $args['minimum'], $args['maximum'] ) ); } } elseif ( ! empty( $args['exclusiveMinimum'] ) && empty( $args['exclusiveMaximum'] ) ) { if ( $value > $args['maximum'] || $value <= $args['minimum'] ) { - return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s must be between %2$d (exclusive) and %2$d (inclusive)', 'woocommerce' ), $param, $args['minimum'], $args['maximum'] ) ); + return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s must be between %2$d (exclusive) and %3$d (inclusive)', 'woocommerce' ), $param, $args['minimum'], $args['maximum'] ) ); } } elseif ( empty( $args['exclusiveMinimum'] ) && empty( $args['exclusiveMaximum'] ) ) { if ( $value > $args['maximum'] || $value < $args['minimum'] ) { - return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s must be between %2$d (inclusive) and %2$d (inclusive)', 'woocommerce' ), $param, $args['minimum'], $args['maximum'] ) ); + return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s must be between %2$d (inclusive) and %3$d (inclusive)', 'woocommerce' ), $param, $args['minimum'], $args['maximum'] ) ); } } } From 8339e9ca255c93ee0bf453547840ccba21f442a5 Mon Sep 17 00:00:00 2001 From: Claudio Sanches Date: Tue, 11 Oct 2016 15:36:53 -0300 Subject: [PATCH 09/12] Introduces wc_get_min_max_price_meta_query() function --- includes/class-wc-query.php | 31 +++------------------ includes/wc-product-functions.php | 37 ++++++++++++++++++++++++++ tests/unit-tests/product/functions.php | 16 +++++++++++ 3 files changed, 57 insertions(+), 27 deletions(-) diff --git a/includes/class-wc-query.php b/includes/class-wc-query.php index 56ed8f9da33..16973da8796 100644 --- a/includes/class-wc-query.php +++ b/includes/class-wc-query.php @@ -545,35 +545,12 @@ class WC_Query { */ private function price_filter_meta_query() { if ( isset( $_GET['max_price'] ) || isset( $_GET['min_price'] ) ) { - $min = isset( $_GET['min_price'] ) ? floatval( $_GET['min_price'] ) : 0; - $max = isset( $_GET['max_price'] ) ? floatval( $_GET['max_price'] ) : 9999999999; + $meta_query = wc_get_min_max_price( $_GET ); + $meta_query['price_filter'] = true; - /** - * Adjust if the store taxes are not displayed how they are stored. - * Max is left alone because the filter was already increased. - * Kicks in when prices excluding tax are displayed including tax. - */ - if ( wc_tax_enabled() && 'incl' === get_option( 'woocommerce_tax_display_shop' ) && ! wc_prices_include_tax() ) { - $tax_classes = array_merge( array( '' ), WC_Tax::get_tax_classes() ); - $class_min = $min; - - foreach ( $tax_classes as $tax_class ) { - if ( $tax_rates = WC_Tax::get_rates( $tax_class ) ) { - $class_min = $min - WC_Tax::get_tax_total( WC_Tax::calc_exclusive_tax( $min, $tax_rates ) ); - } - } - - $min = $class_min; - } - - return array( - 'key' => '_price', - 'value' => array( $min, $max ), - 'compare' => 'BETWEEN', - 'type' => 'DECIMAL', - 'price_filter' => true, - ); + return $meta_query; } + return array(); } diff --git a/includes/wc-product-functions.php b/includes/wc-product-functions.php index 0c89e29bb48..05c7f896035 100644 --- a/includes/wc-product-functions.php +++ b/includes/wc-product-functions.php @@ -751,3 +751,40 @@ function wc_get_product_visibility_options() { 'hidden' => __( 'Hidden', 'woocommerce' ), ) ); } + +/** + * Get min/max price meta query args. + * + * @since 2.7.0 + * @param array $args Min price and max price arguments. + * @return array + */ +function wc_get_min_max_price_meta_query( $args ) { + $min = isset( $args['min_price'] ) ? floatval( $args['min_price'] ) : 0; + $max = isset( $args['max_price'] ) ? floatval( $args['max_price'] ) : 9999999999; + + /** + * Adjust if the store taxes are not displayed how they are stored. + * Max is left alone because the filter was already increased. + * Kicks in when prices excluding tax are displayed including tax. + */ + if ( wc_tax_enabled() && 'incl' === get_option( 'woocommerce_tax_display_shop' ) && ! wc_prices_include_tax() ) { + $tax_classes = array_merge( array( '' ), WC_Tax::get_tax_classes() ); + $class_min = $min; + + foreach ( $tax_classes as $tax_class ) { + if ( $tax_rates = WC_Tax::get_rates( $tax_class ) ) { + $class_min = $min - WC_Tax::get_tax_total( WC_Tax::calc_exclusive_tax( $min, $tax_rates ) ); + } + } + + $min = $class_min; + } + + return array( + 'key' => '_price', + 'value' => array( $min, $max ), + 'compare' => 'BETWEEN', + 'type' => 'DECIMAL', + ); +} diff --git a/tests/unit-tests/product/functions.php b/tests/unit-tests/product/functions.php index 0ce7de86654..038215aa530 100644 --- a/tests/unit-tests/product/functions.php +++ b/tests/unit-tests/product/functions.php @@ -183,4 +183,20 @@ class WC_Tests_Product_Functions extends WC_Unit_Test_Case { // Delete Product WC_Helper_Product::delete_product( $product->id ); } + + /** + * Test wc_get_min_max_price_meta_query() + * + * @since 2.7.0 + */ + public function test_wc_get_min_max_price_meta_query() { + $meta_query = wc_get_min_max_price_meta_query( array( 'min_price' => 10, 'max_price' => 100 ) ); + + $this->assertEquals( array( + 'key' => '_price', + 'value' => array( 10, 100 ), + 'compare' => 'BETWEEN', + 'type' => 'DECIMAL', + ), $meta_query ); + } } From 21f7bfe7bc624a03104a0a6097032e87a049c7cd Mon Sep 17 00:00:00 2001 From: Claudio Sanches Date: Tue, 11 Oct 2016 15:42:01 -0300 Subject: [PATCH 10/12] Fixed price meta_query --- includes/class-wc-query.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/class-wc-query.php b/includes/class-wc-query.php index 16973da8796..cc1de8f9ddd 100644 --- a/includes/class-wc-query.php +++ b/includes/class-wc-query.php @@ -545,7 +545,7 @@ class WC_Query { */ private function price_filter_meta_query() { if ( isset( $_GET['max_price'] ) || isset( $_GET['min_price'] ) ) { - $meta_query = wc_get_min_max_price( $_GET ); + $meta_query = wc_get_min_max_price_meta_query( $_GET ); $meta_query['price_filter'] = true; return $meta_query; From ec79238f308db7f816aef3ed45ce47bd02981683 Mon Sep 17 00:00:00 2001 From: Claudio Sanches Date: Tue, 11 Oct 2016 15:42:35 -0300 Subject: [PATCH 11/12] Added min_price and max_price product filter --- .../api/class-wc-rest-products-controller.php | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/includes/api/class-wc-rest-products-controller.php b/includes/api/class-wc-rest-products-controller.php index a11b77b2e27..b9e5d397b26 100644 --- a/includes/api/class-wc-rest-products-controller.php +++ b/includes/api/class-wc-rest-products-controller.php @@ -210,6 +210,11 @@ class WC_REST_Products_Controller extends WC_REST_Posts_Controller { ) ); } + // Price filter. + if ( ! empty( $request['min_price'] ) || ! empty( $request['max_price'] ) ) { + $args['meta_query'] = $this->add_meta_query( $args, wc_get_min_max_price_meta_query( $request ) ); + } + // Filter product in stock or out of stock. if ( is_bool( $request['in_stock'] ) ) { $args['meta_query'] = $this->add_meta_query( $args, array( @@ -2769,6 +2774,18 @@ class WC_REST_Products_Controller extends WC_REST_Posts_Controller { 'sanitize_callback' => 'wc_string_to_bool', 'validate_callback' => 'rest_validate_request_arg', ); + $params['min_price'] = array( + 'description' => __( 'Limit result set to products based on a minimum price.', 'woocommerce' ), + 'type' => 'string', + 'sanitize_callback' => 'sanitize_text_field', + 'validate_callback' => 'rest_validate_request_arg', + ); + $params['max_price'] = array( + 'description' => __( 'Limit result set to products based on a maximum price.', 'woocommerce' ), + 'type' => 'string', + 'sanitize_callback' => 'sanitize_text_field', + 'validate_callback' => 'rest_validate_request_arg', + ); return $params; } From 507bbffc66464b7aad13c846e0860d895f1532c3 Mon Sep 17 00:00:00 2001 From: Claudio Sanches Date: Tue, 11 Oct 2016 15:45:49 -0300 Subject: [PATCH 12/12] Fixed coding standards --- .../api/class-wc-rest-products-controller.php | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/includes/api/class-wc-rest-products-controller.php b/includes/api/class-wc-rest-products-controller.php index b9e5d397b26..120f8d2f1fa 100644 --- a/includes/api/class-wc-rest-products-controller.php +++ b/includes/api/class-wc-rest-products-controller.php @@ -187,26 +187,24 @@ class WC_REST_Products_Controller extends WC_REST_Posts_Controller { // Filter by sku. if ( ! empty( $request['sku'] ) ) { $args['meta_query'] = $this->add_meta_query( $args, array( - 'key' => '_sku', - 'value' => $request['sku'], - 'compare' => '=', + 'key' => '_sku', + 'value' => $request['sku'], ) ); } // Filter featured. if ( is_bool( $request['featured'] ) ) { $args['meta_query'] = $this->add_meta_query( $args, array( - 'key' => '_featured', - 'value' => true === $request['featured'] ? 'yes' : 'no', + 'key' => '_featured', + 'value' => true === $request['featured'] ? 'yes' : 'no', ) ); } // Filter by tax class. if ( ! empty( $request['tax_class'] ) ) { $args['meta_query'] = $this->add_meta_query( $args, array( - 'key' => '_tax_class', - 'value' => 'standard' !== $request['tax_class'] ? $request['tax_class'] : '', - 'compare' => '=', + 'key' => '_tax_class', + 'value' => 'standard' !== $request['tax_class'] ? $request['tax_class'] : '', ) ); } @@ -218,8 +216,8 @@ class WC_REST_Products_Controller extends WC_REST_Posts_Controller { // Filter product in stock or out of stock. if ( is_bool( $request['in_stock'] ) ) { $args['meta_query'] = $this->add_meta_query( $args, array( - 'key' => '_stock_status', - 'value' => true === $request['in_stock'] ? 'instock' : 'outofstock', + 'key' => '_stock_status', + 'value' => true === $request['in_stock'] ? 'instock' : 'outofstock', ) ); }