Skip to content
This repository has been archived by the owner on Feb 23, 2024. It is now read-only.

Commit

Permalink
Refactor cart error getters as wrappers to the validate functions
Browse files Browse the repository at this point in the history
  • Loading branch information
xristos3490 authored and xristos3490 committed Feb 21, 2022
1 parent 4457289 commit 578fcae
Show file tree
Hide file tree
Showing 5 changed files with 232 additions and 118 deletions.
4 changes: 2 additions & 2 deletions src/StoreApi/Routes/AbstractRoute.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
namespace Automattic\WooCommerce\Blocks\StoreApi\Routes;

use Automattic\WooCommerce\Blocks\StoreApi\Schemas\AbstractSchema;
use Automattic\WooCommerce\Blocks\StoreApi\Utilities\InvalidStockLevelsInCartException;
use Automattic\WooCommerce\Blocks\StoreApi\Utilities\InvalidCartException;
use WP_Error;

/**
Expand Down Expand Up @@ -71,7 +71,7 @@ public function get_response( \WP_REST_Request $request ) {
}
} catch ( RouteException $error ) {
$response = $this->get_route_error_response( $error->getErrorCode(), $error->getMessage(), $error->getCode(), $error->getAdditionalData() );
} catch ( InvalidStockLevelsInCartException $error ) {
} catch ( InvalidCartException $error ) {
$response = $this->get_route_error_response_from_object( $error->getError(), $error->getCode(), $error->getAdditionalData() );
} catch ( \Exception $error ) {
$response = $this->get_route_error_response( 'unknown_server_error', $error->getMessage(), 500 );
Expand Down
3 changes: 1 addition & 2 deletions src/StoreApi/Routes/Checkout.php
Original file line number Diff line number Diff line change
Expand Up @@ -151,8 +151,7 @@ protected function get_route_post_response( \WP_REST_Request $request ) {
* Validate items etc are allowed in the order before the order is processed. This will fix violations and tell
* the customer.
*/
$this->cart_controller->validate_cart_items();
$this->cart_controller->validate_cart_coupons();
$this->cart_controller->validate_cart();

/**
* Obtain Draft Order and process request data.
Expand Down
25 changes: 2 additions & 23 deletions src/StoreApi/Schemas/CartSchema.php
Original file line number Diff line number Diff line change
Expand Up @@ -403,29 +403,8 @@ protected function get_tax_lines( $cart ) {
* @return array
*/
protected function get_cart_errors( $cart ) {
$controller = new CartController();
$cart_errors = [];
$item_errors = $controller->get_cart_item_errors();
$coupon_errors = $controller->get_cart_coupon_errors();

try {
/**
* Fire action to validate cart. Functions hooking into this should throw a \RouteException.
* @example See docs/examples/validate-cart.md
*
* @param \WC_Cart $cart Cart object.
*/
do_action( '__experimental_woocommerce_store_api_validate_cart', $cart );
} catch ( RouteException $error ) {
$cart_errors[] = new WP_Error( $error->getErrorCode(), $error->getMessage() );
}

$cart_errors = array_filter(
array_merge( $cart_errors, $item_errors, $coupon_errors ),
function ( WP_Error $error ) {
return $error->has_errors();
}
);
$controller = new CartController();
$cart_errors = $controller->get_cart_errors();

return array_values( array_map( [ $this->error_schema, 'get_item_response' ], $cart_errors ) );
}
Expand Down
244 changes: 153 additions & 91 deletions src/StoreApi/Utilities/CartController.php
Original file line number Diff line number Diff line change
Expand Up @@ -355,15 +355,52 @@ private function get_error_message_for_stock_exception_type( $exception_type, $s
return __( 'There was an error with an item in your cart.', 'woo-gutenberg-products-block' );
}

/**
* Validate cart and check for errors.
*
* @throws InvalidCartException Exception if invalid data is detected in the cart.
* @throws RouteException Exception if invalid data is detected in the cart.
*/
public function validate_cart() {

$cart = $this->get_cart_instance();
$this->validate_cart_items();
$this->validate_cart_coupons();

/**
* Fire action to validate cart. Functions hooking into this should throw a \InvalidCartException.
*
* @example See docs/examples/validate-cart.md
*
* @param \WC_Cart $cart Cart object.
*/
do_action( '__experimental_woocommerce_store_api_validate_cart', $cart );

// Before running the woocommerce_check_cart_items hook, unhook validation from the core cart.
remove_action( 'woocommerce_check_cart_items', array( $cart, 'check_cart_items' ), 1 );
remove_action( 'woocommerce_check_cart_items', array( $cart, 'check_cart_coupons' ), 1 );

/**
* Fires when cart items are being validated.
*
* Allow 3rd parties to validate cart items. This is a legacy hook from Woo core.
* This filter will be deprecated because it encourages usage of wc_add_notice. For the API we need to capture
* notices and convert to exceptions instead.
*/
do_action( 'woocommerce_check_cart_items' );
NoticeHandler::convert_notices_to_exceptions( 'woocommerce_rest_cart_item_error' );
}

/**
* Validate all items in the cart and check for errors.
*
* @throws InvalidStockLevelsInCartException Exception if invalid data is detected due to insufficient stock levels.
* @throws InvalidCartException Exception if invalid data is detected due to insufficient stock levels.
*/
public function validate_cart_items() {
$cart = $this->get_cart_instance();
$cart_items = $this->get_cart_items();

$errors = [];
$out_of_stock_products = [];
$too_many_in_cart_products = [];
$partial_out_of_stock_products = [];
Expand All @@ -372,6 +409,8 @@ public function validate_cart_items() {
foreach ( $cart_items as $cart_item_key => $cart_item ) {
try {
$this->validate_cart_item( $cart_item );
} catch ( RouteException $error ) {
$errors[] = new WP_Error( $error->getErrorCode(), $error->getMessage(), $error->getAdditionalData() );
} catch ( TooManyInCartException $error ) {
$too_many_in_cart_products[] = $error;
} catch ( NotPurchasableException $error ) {
Expand All @@ -383,77 +422,52 @@ public function validate_cart_items() {
}
}

$error = new WP_Error();

if ( count( $out_of_stock_products ) > 0 ) {
$singular_error = $this->get_error_message_for_stock_exception_type( 'out_of_stock', 'singular' );
$plural_error = $this->get_error_message_for_stock_exception_type( 'out_of_stock', 'plural' );

$error->add(
409,
$this->add_product_names_to_message( $singular_error, $plural_error, $out_of_stock_products )
);
}
if ( count( $errors ) > 0 ) {

if ( count( $not_purchasable_products ) > 0 ) {
$singular_error = $this->get_error_message_for_stock_exception_type( 'not_purchasable', 'singular' );
$plural_error = $this->get_error_message_for_stock_exception_type( 'not_purchasable', 'plural' );
$error = new WP_Error();
foreach ( $errors as $wp_error ) {
$error->add(
$wp_error->get_error_code(),
$wp_error->get_error_message(),
$wp_error->get_error_data()
);
}

$error->add(
409,
$this->add_product_names_to_message( $singular_error, $plural_error, $not_purchasable_products )
throw new InvalidCartException(
'woocommerce_cart_error',
$error,
409
);
}

if ( count( $too_many_in_cart_products ) > 0 ) {
$singular_error = $this->get_error_message_for_stock_exception_type( 'too_many_in_cart', 'singular' );
$plural_error = $this->get_error_message_for_stock_exception_type( 'too_many_in_cart', 'plural' );
$errors = $this->stock_exceptions_to_wp_errors( $too_many_in_cart_products, $not_purchasable_products, $partial_out_of_stock_products, $out_of_stock_products );

$error->add(
409,
$this->add_product_names_to_message( $singular_error, $plural_error, $too_many_in_cart_products )
);
}

if ( count( $partial_out_of_stock_products ) > 0 ) {
$singular_error = $this->get_error_message_for_stock_exception_type( 'partial_out_of_stock', 'singular' );
$plural_error = $this->get_error_message_for_stock_exception_type( 'partial_out_of_stock', 'plural' );
if ( ! empty( $errors ) ) {

$error->add(
409,
$this->add_product_names_to_message( $singular_error, $plural_error, $partial_out_of_stock_products )
);
}
$error = new WP_Error();
foreach ( $errors as $wp_error ) {
$error->add(
$wp_error->get_error_code(),
$wp_error->get_error_message(),
$wp_error->get_error_data()
);
}

if ( $error->has_errors() ) {
throw new InvalidStockLevelsInCartException(
throw new InvalidCartException(
'woocommerce_stock_availability_error',
$error
$error,
409
);
}

// Before running the woocommerce_check_cart_items hook, unhook validation from the core cart.
remove_action( 'woocommerce_check_cart_items', array( $cart, 'check_cart_items' ), 1 );
remove_action( 'woocommerce_check_cart_items', array( $cart, 'check_cart_coupons' ), 1 );

/**
* Fires when cart items are being validated.
*
* Allow 3rd parties to validate cart items. This is a legacy hook from Woo core.
* This filter will be deprecated because it encourages usage of wc_add_notice. For the API we need to capture
* notices and convert to exceptions instead.
*/
do_action( 'woocommerce_check_cart_items' );
NoticeHandler::convert_notices_to_exceptions( 'woocommerce_rest_cart_item_error' );
}

/**
* This method will take arrays of exceptions relating to stock, and will convert them to a WP_Error object.
*
* @param TooManyInCartException[] $too_many_in_cart_products Array of TooManyInCartExceptions.
* @param NotPurchasableException[] $not_purchasable_products Array of NotPurchasableExceptions.
* @param TooManyInCartException[] $too_many_in_cart_products Array of TooManyInCartExceptions.
* @param NotPurchasableException[] $not_purchasable_products Array of NotPurchasableExceptions.
* @param PartialOutOfStockException[] $partial_out_of_stock_products Array of PartialOutOfStockExceptions.
* @param OutOfStockException[] $out_of_stock_products Array of OutOfStockExceptions.
* @param OutOfStockException[] $out_of_stock_products Array of OutOfStockExceptions.
*
* @return WP_Error[] The WP_Error object returned. Will have errors if any exceptions were in the args. It will be empty if they do not.
*/
Expand Down Expand Up @@ -580,69 +594,117 @@ public function validate_cart_item( $cart_item ) {
/**
* Validate all coupons in the cart and check for errors.
*
* @throws RouteException Exception if invalid data is detected.
* @throws InvalidCartException Exception if invalid data is detected.
*/
public function validate_cart_coupons() {
$cart_coupons = $this->get_cart_coupons();
$errors = [];

foreach ( $cart_coupons as $code ) {
$coupon = new \WC_Coupon( $code );
$this->validate_cart_coupon( $coupon );
try {
$this->validate_cart_coupon( $coupon );
} catch ( RouteException $error ) {
$errors[] = new \WP_Error( $error->getErrorCode(), $error->getMessage(), $error->getAdditionalData() );
}
}

if ( ! empty( $errors ) ) {

$error = new WP_Error();
foreach ( $errors as $wp_error ) {
$error->add(
$wp_error->get_error_code(),
$wp_error->get_error_message(),
$wp_error->get_error_data()
);
}

throw new InvalidCartException(
'woocommerce_coupons_error',
$error,
409
);
}
}

/**
* Validate the cart and get a list of errors.
*
* @return WP_Error[] An array of WP_Errors describing the cart's error state.
*/
public function get_cart_errors() {
$errors = [];

try {
$this->validate_cart();
} catch ( RouteException $error ) {
$errors[] = new \WP_Error( $error->getErrorCode(), $error->getMessage(), $error->getAdditionalData() );
} catch ( InvalidCartException $error ) {

$wp_error = $error->getError();
foreach ( (array) $wp_error->errors as $code => $messages ) {
foreach ( (array) $messages as $message ) {
$additional_data = $wp_error->get_error_data( $code );
$errors[] = new \WP_Error( $code, $message, $additional_data );
}
}
} catch ( \Exception $error ) {
$errors[] = new \WP_Error( $error->getCode(), $error->getMessage() );
}

$cart_errors = array_filter(
$errors,
function ( WP_Error $error ) {
return $error->has_errors();
}
);

return $cart_errors;
}

/**
* Validate all items in the cart and get a list of errors.
*
* @return WP_Error[] An array of WP_Errors describing the cart's error state.
*/
public function get_cart_item_errors() {
$errors = [];
$cart_items = $this->get_cart_items();
$errors = [];

$too_many_in_cart_exceptions = [];
$not_purchasable_exceptions = [];
$partial_out_of_stock_exceptions = [];
$out_of_stock_exceptions = [];
try {
$this->validate_cart_items();
} catch ( InvalidCartException $error ) {

foreach ( $cart_items as $cart_item_key => $cart_item ) {
try {
$this->validate_cart_item( $cart_item );
} catch ( RouteException $error ) {
$errors[] = new WP_Error( $error->getErrorCode(), $error->getMessage() );
} catch ( TooManyInCartException $error ) {
$too_many_in_cart_exceptions[] = $error;
} catch ( NotPurchasableException $error ) {
$not_purchasable_exceptions[] = $error;
} catch ( PartialOutOfStockException $error ) {
$partial_out_of_stock_exceptions[] = $error;
} catch ( OutOfStockException $error ) {
$out_of_stock_exceptions[] = $error;
$wp_error = $error->getError();
foreach ( (array) $wp_error->errors as $code => $messages ) {
foreach ( (array) $messages as $message ) {
$additional_data = $wp_error->get_error_data( $code );
$errors[] = new \WP_Error( $code, $message, $additional_data );
}
}
}

if ( count( $errors ) > 0 ) {
return $errors;
}

return $this->stock_exceptions_to_wp_errors( $too_many_in_cart_exceptions, $not_purchasable_exceptions, $partial_out_of_stock_exceptions, $out_of_stock_exceptions );
return $errors;
}

/**
* Validate all items in the cart and get a list of errors.
* Validate all coupons in the cart and get a list of errors.
*
* @throws RouteException Exception if invalid data is detected.
* @return WP_Error[] An array of WP_Errors describing coupons error state.
*/
public function get_cart_coupon_errors() {
$errors = [];
$cart_coupons = $this->get_cart_coupons();
$errors = [];

foreach ( $cart_coupons as $code ) {
try {
$coupon = new \WC_Coupon( $code );
$this->validate_cart_coupon( $coupon );
} catch ( RouteException $error ) {
$errors[] = new \WP_Error( $error->getErrorCode(), $error->getMessage() );
try {
$this->validate_cart_coupons();
} catch ( InvalidCartException $error ) {

$wp_error = $error->getError();
foreach ( (array) $wp_error->errors as $code => $messages ) {
foreach ( (array) $messages as $message ) {
$additional_data = $wp_error->get_error_data( $code );
$errors[] = new \WP_Error( $code, $message, $additional_data );
}
}
}

Expand Down
Loading

0 comments on commit 578fcae

Please sign in to comment.