Sync order data with cart data when cart is updated from any route (https://github.com/woocommerce/woocommerce-blocks/pull/5379)

* Link order controller to cart routes

* Remove order controller from checkout route

* Fix PHP warnings in abstract schema

* Fix PHP warnings in abstract route

* Update shipping phone handling

* Includes are handled in core now

* Remove maybe_update_order_from_customer

* Add cart_updated routine to all cart routes

* Remove abstract method

* Remove test for woocommerce_blocks_cart_update_order_from_customer_request

* Remove do_order_callback
This commit is contained in:
Mike Jolley 2021-12-21 13:11:51 +00:00 committed by GitHub
parent 14a45a4380
commit ecc80e5cff
11 changed files with 78 additions and 123 deletions

View File

@ -268,11 +268,11 @@
}
},
{
"name": "woocommerce_blocks_cart_update_order_from_customer_request",
"file": "StoreApi/Routes/CartUpdateCustomer.php",
"name": "woocommerce_blocks_cart_update_order_from_request",
"file": "StoreApi/Routes/AbstractCartRoute.php",
"type": "action",
"doc": {
"description": "Fires when the Checkout Block/Store API updates an existing draft order from customer data.",
"description": "Fires when the order is synced with cart data from a cart route.",
"long_description": "",
"tags": [
{

View File

@ -19,7 +19,7 @@
- [woocommerce_blocks_cart_enqueue_data](#woocommerce_blocks_cart_enqueue_data)
- [woocommerce_blocks_cart_enqueue_data](#woocommerce_blocks_cart_enqueue_data-1)
- [woocommerce_blocks_cart_update_customer_from_request](#woocommerce_blocks_cart_update_customer_from_request)
- [woocommerce_blocks_cart_update_order_from_customer_request](#woocommerce_blocks_cart_update_order_from_customer_request)
- [woocommerce_blocks_cart_update_order_from_request](#woocommerce_blocks_cart_update_order_from_request)
- [woocommerce_blocks_checkout_enqueue_data](#woocommerce_blocks_checkout_enqueue_data)
- [woocommerce_blocks_checkout_order_processed](#woocommerce_blocks_checkout_order_processed)
- [woocommerce_blocks_checkout_update_order_from_request](#woocommerce_blocks_checkout_update_order_from_request)
@ -261,13 +261,13 @@ File: [StoreApi/Routes/CartUpdateCustomer.php](../src/StoreApi/Routes/CartUpdate
---
## woocommerce_blocks_cart_update_order_from_customer_request
## woocommerce_blocks_cart_update_order_from_request
Fires when the Checkout Block/Store API updates an existing draft order from customer data.
Fires when the order is synced with cart data from a cart route.
```php
do_action( 'woocommerce_blocks_cart_update_order_from_customer_request', \WC_Order $draft_order, \WC_Customer $customer, \WP_REST_Request $request )
do_action( 'woocommerce_blocks_cart_update_order_from_request', \WC_Order $draft_order, \WC_Customer $customer, \WP_REST_Request $request )
```
### Parameters
@ -281,7 +281,7 @@ do_action( 'woocommerce_blocks_cart_update_order_from_customer_request', \WC_Ord
### Source
File: [StoreApi/Routes/CartUpdateCustomer.php](../src/StoreApi/Routes/CartUpdateCustomer.php)
File: [StoreApi/Routes/AbstractCartRoute.php](../src/StoreApi/Routes/AbstractCartRoute.php)
---

View File

@ -4,13 +4,16 @@ namespace Automattic\WooCommerce\Blocks\StoreApi\Routes;
use Automattic\WooCommerce\Blocks\StoreApi\Schemas\AbstractSchema;
use Automattic\WooCommerce\Blocks\StoreApi\Schemas\CartSchema;
use Automattic\WooCommerce\Blocks\StoreApi\Utilities\CartController;
use Automattic\WooCommerce\Blocks\StoreApi\Utilities\DraftOrderTrait;
use Automattic\WooCommerce\Blocks\StoreApi\Utilities\OrderController;
/**
* Abstract Cart Route
*
* @internal This API is used internally by Blocks--it is still in flux and may be subject to revisions.
*/
abstract class AbstractCartRoute extends AbstractRoute {
use DraftOrderTrait;
/**
* Schema class for this route's response.
*
@ -32,18 +35,27 @@ abstract class AbstractCartRoute extends AbstractRoute {
*/
protected $cart_controller;
/**
* Order controller class instance.
*
* @var OrderController
*/
protected $order_controller;
/**
* Constructor accepts two types of schema; one for the item being returned, and one for the cart as a whole. These
* may be the same depending on the route.
*
* @param CartSchema $cart_schema Schema class for the cart.
* @param AbstractSchema $item_schema Schema class for this route's items if it differs from the cart schema.
* @param CartController $cart_controller Cart controller class.
* @param CartSchema $cart_schema Schema class for the cart.
* @param AbstractSchema $item_schema Schema class for this route's items if it differs from the cart schema.
* @param CartController $cart_controller Cart controller class.
* @param OrderController $order_controller Order controller class.
*/
public function __construct( CartSchema $cart_schema, AbstractSchema $item_schema = null, CartController $cart_controller ) {
$this->schema = is_null( $item_schema ) ? $cart_schema : $item_schema;
$this->cart_schema = $cart_schema;
$this->cart_controller = $cart_controller;
public function __construct( CartSchema $cart_schema, AbstractSchema $item_schema = null, CartController $cart_controller, OrderController $order_controller ) {
$this->schema = is_null( $item_schema ) ? $cart_schema : $item_schema;
$this->cart_schema = $cart_schema;
$this->cart_controller = $cart_controller;
$this->order_controller = $order_controller;
}
/**
@ -74,6 +86,8 @@ abstract class AbstractCartRoute extends AbstractRoute {
if ( is_wp_error( $response ) ) {
$response = $this->error_to_response( $response );
} elseif ( in_array( $request->get_method(), [ 'POST', 'PUT', 'PATCH', 'DELETE' ], true ) ) {
$this->cart_updated( $request );
}
return $this->add_nonce_headers( $response );
@ -102,6 +116,29 @@ abstract class AbstractCartRoute extends AbstractRoute {
return 'GET' !== $request->get_method();
}
/**
* Triggered after an update to cart data. Re-calculates totals and updates draft orders (if they already exist) to
* keep all data in sync.
*
* @param \WP_REST_Request $request Request object.
*/
protected function cart_updated( \WP_REST_Request $request ) {
$draft_order = $this->get_draft_order();
if ( $draft_order ) {
$this->order_controller->update_order_from_cart( $draft_order );
/**
* Fires when the order is synced with cart data from a cart route.
*
* @param \WC_Order $draft_order Order object.
* @param \WC_Customer $customer Customer object.
* @param \WP_REST_Request $request Full details about the request.
*/
do_action( 'woocommerce_blocks_cart_update_order_from_request', $draft_order, $request );
}
}
/**
* Ensures the cart totals are calculated before an API response is generated.
*/

View File

@ -87,8 +87,8 @@ abstract class AbstractRoute implements RouteInterface {
/**
* Converts an error to a response object. Based on \WP_REST_Server.
*
* @param WP_Error $error WP_Error instance.
* @return WP_REST_Response List of associative arrays with code and message keys.
* @param \WP_Error $error WP_Error instance.
* @return \WP_REST_Response List of associative arrays with code and message keys.
*/
protected function error_to_response( $error ) {
$error_data = $error->get_error_data();

View File

@ -121,7 +121,6 @@ class CartUpdateCustomer extends AbstractCartRoute {
$customer->save();
$this->calculate_totals();
$this->maybe_update_order_from_customer( $customer, $request );
return rest_ensure_response( $this->schema->get_item_response( $cart ) );
}
@ -146,56 +145,4 @@ class CartUpdateCustomer extends AbstractCartRoute {
'phone' => $customer->get_billing_phone(),
];
}
/**
* If there is a draft order, update the customer data within that also so the
* cart and order are kept in sync.
*
* @param \WC_Customer $customer Customer object.
* @param \WP_REST_Request $request Request object.
*/
protected function maybe_update_order_from_customer( \WC_Customer $customer, \WP_REST_Request $request ) {
$draft_order = $this->get_draft_order();
if ( ! $draft_order ) {
return;
}
$draft_order->set_props(
[
'billing_first_name' => $customer->get_billing_first_name(),
'billing_last_name' => $customer->get_billing_last_name(),
'billing_company' => $customer->get_billing_company(),
'billing_address_1' => $customer->get_billing_address_1(),
'billing_address_2' => $customer->get_billing_address_2(),
'billing_city' => $customer->get_billing_city(),
'billing_state' => $customer->get_billing_state(),
'billing_postcode' => $customer->get_billing_postcode(),
'billing_country' => $customer->get_billing_country(),
'billing_email' => $customer->get_billing_email(),
'billing_phone' => $customer->get_billing_phone(),
'shipping_first_name' => $customer->get_shipping_first_name(),
'shipping_last_name' => $customer->get_shipping_last_name(),
'shipping_company' => $customer->get_shipping_company(),
'shipping_address_1' => $customer->get_shipping_address_1(),
'shipping_address_2' => $customer->get_shipping_address_2(),
'shipping_city' => $customer->get_shipping_city(),
'shipping_state' => $customer->get_shipping_state(),
'shipping_postcode' => $customer->get_shipping_postcode(),
'shipping_country' => $customer->get_shipping_country(),
'shipping_phone' => $customer->get_shipping_phone(),
]
);
/**
* Fires when the Checkout Block/Store API updates an existing draft order from customer data.
*
* @param \WC_Order $draft_order Order object.
* @param \WC_Customer $customer Customer object.
* @param \WP_REST_Request $request Full details about the request.
*/
do_action( 'woocommerce_blocks_cart_update_order_from_customer_request', $draft_order, $customer, $request );
$draft_order->save();
}
}

View File

@ -13,7 +13,6 @@ use Automattic\WooCommerce\Blocks\StoreApi\Utilities\InvalidStockLevelsInCartExc
use Automattic\WooCommerce\Blocks\StoreApi\Utilities\OrderController;
use Automattic\WooCommerce\Checkout\Helpers\ReserveStock;
use Automattic\WooCommerce\Checkout\Helpers\ReserveStockException;
/**
* Checkout class.
*
@ -29,29 +28,6 @@ class Checkout extends AbstractCartRoute {
*/
private $order = null;
/**
* Order controller class instance.
*
* @var OrderController
*/
protected $order_controller;
/**
* Constructor accepts two types of schema; one for the item being returned, and one for the cart as a whole. These
* may be the same depending on the route.
*
* @param CartSchema $cart_schema Schema class for the cart.
* @param AbstractSchema $item_schema Schema class for this route's items if it differs from the cart schema.
* @param CartController $cart_controller Cart controller class.
* @param OrderController $order_controller Order controller class.
*/
public function __construct( CartSchema $cart_schema, AbstractSchema $item_schema = null, CartController $cart_controller, OrderController $order_controller ) {
$this->schema = is_null( $item_schema ) ? $cart_schema : $item_schema;
$this->cart_schema = $cart_schema;
$this->cart_controller = $cart_controller;
$this->order_controller = $order_controller;
}
/**
* Get the path of this REST route.
*

View File

@ -75,19 +75,19 @@ class RoutesController {
$order_controller = new OrderController();
$this->routes = [
'cart' => new Routes\Cart( $this->schemas->get( 'cart' ), null, $cart_controller ),
'cart-add-item' => new Routes\CartAddItem( $this->schemas->get( 'cart' ), null, $cart_controller ),
'cart-apply-coupon' => new Routes\CartApplyCoupon( $this->schemas->get( 'cart' ), null, $cart_controller ),
'cart-coupons' => new Routes\CartCoupons( $this->schemas->get( 'cart' ), $this->schemas->get( 'cart-coupon' ), $cart_controller ),
'cart-coupons-by-code' => new Routes\CartCouponsByCode( $this->schemas->get( 'cart' ), $this->schemas->get( 'cart-coupon' ), $cart_controller ),
'cart-extensions' => new Routes\CartExtensions( $this->schemas->get( 'cart' ), $this->schemas->get( 'cart-extensions' ), $cart_controller ),
'cart-items' => new Routes\CartItems( $this->schemas->get( 'cart' ), $this->schemas->get( 'cart-item' ), $cart_controller ),
'cart-items-by-key' => new Routes\CartItemsByKey( $this->schemas->get( 'cart' ), $this->schemas->get( 'cart-item' ), $cart_controller ),
'cart-remove-coupon' => new Routes\CartRemoveCoupon( $this->schemas->get( 'cart' ), null, $cart_controller ),
'cart-remove-item' => new Routes\CartRemoveItem( $this->schemas->get( 'cart' ), null, $cart_controller ),
'cart-select-shipping-rate' => new Routes\CartSelectShippingRate( $this->schemas->get( 'cart' ), null, $cart_controller ),
'cart-update-item' => new Routes\CartUpdateItem( $this->schemas->get( 'cart' ), null, $cart_controller ),
'cart-update-customer' => new Routes\CartUpdateCustomer( $this->schemas->get( 'cart' ), null, $cart_controller ),
'cart' => new Routes\Cart( $this->schemas->get( 'cart' ), null, $cart_controller, $order_controller ),
'cart-add-item' => new Routes\CartAddItem( $this->schemas->get( 'cart' ), null, $cart_controller, $order_controller ),
'cart-apply-coupon' => new Routes\CartApplyCoupon( $this->schemas->get( 'cart' ), null, $cart_controller, $order_controller ),
'cart-coupons' => new Routes\CartCoupons( $this->schemas->get( 'cart' ), $this->schemas->get( 'cart-coupon' ), $cart_controller, $order_controller ),
'cart-coupons-by-code' => new Routes\CartCouponsByCode( $this->schemas->get( 'cart' ), $this->schemas->get( 'cart-coupon' ), $cart_controller, $order_controller ),
'cart-extensions' => new Routes\CartExtensions( $this->schemas->get( 'cart' ), $this->schemas->get( 'cart-extensions' ), $cart_controller, $order_controller ),
'cart-items' => new Routes\CartItems( $this->schemas->get( 'cart' ), $this->schemas->get( 'cart-item' ), $cart_controller, $order_controller ),
'cart-items-by-key' => new Routes\CartItemsByKey( $this->schemas->get( 'cart' ), $this->schemas->get( 'cart-item' ), $cart_controller, $order_controller ),
'cart-remove-coupon' => new Routes\CartRemoveCoupon( $this->schemas->get( 'cart' ), null, $cart_controller, $order_controller ),
'cart-remove-item' => new Routes\CartRemoveItem( $this->schemas->get( 'cart' ), null, $cart_controller, $order_controller ),
'cart-select-shipping-rate' => new Routes\CartSelectShippingRate( $this->schemas->get( 'cart' ), null, $cart_controller, $order_controller ),
'cart-update-item' => new Routes\CartUpdateItem( $this->schemas->get( 'cart' ), null, $cart_controller, $order_controller ),
'cart-update-customer' => new Routes\CartUpdateCustomer( $this->schemas->get( 'cart' ), null, $cart_controller, $order_controller ),
'checkout' => new Routes\Checkout( $this->schemas->get( 'cart' ), $this->schemas->get( 'checkout' ), $cart_controller, $order_controller ),
'product-attributes' => new Routes\ProductAttributes( $this->schemas->get( 'product-attribute' ) ),
'product-attributes-by-id' => new Routes\ProductAttributesById( $this->schemas->get( 'product-attribute' ) ),

View File

@ -1,8 +1,8 @@
<?php
namespace Automattic\WooCommerce\Blocks\StoreApi\Schemas;
use Automattic\WooCommerce\Blocks\Package;
use Automattic\WooCommerce\Blocks\Domain\Services\ExtendRestApi;
/**
* AbstractSchema class.
*
@ -56,6 +56,13 @@ abstract class AbstractSchema {
);
}
/**
* Return schema properties.
*
* @return array
*/
abstract public function get_properties();
/**
* Recursive removal of arg_options.
*
@ -94,7 +101,7 @@ abstract class AbstractSchema {
/**
* Returns extended data for a specific endpoint.
*
* @param string $endpoint The endpoint identifer.
* @param string $endpoint The endpoint identifier.
* @param array ...$passed_args An array of arguments to be passed to callbacks.
* @return object the data that will get added.
*/

View File

@ -23,8 +23,6 @@ class CartController {
*/
public function load_cart() {
if ( ! did_action( 'woocommerce_load_cart_from_session' ) && function_exists( 'wc_load_cart' ) ) {
include_once WC_ABSPATH . 'includes/wc-cart-functions.php';
include_once WC_ABSPATH . 'includes/wc-notice-functions.php';
wc_load_cart();
}
}

View File

@ -505,15 +505,8 @@ class OrderController {
'shipping_state' => wc()->customer->get_shipping_state(),
'shipping_postcode' => wc()->customer->get_shipping_postcode(),
'shipping_country' => wc()->customer->get_shipping_country(),
'shipping_phone' => wc()->customer->get_shipping_phone(),
]
);
$shipping_phone_value = is_callable( [ wc()->customer, 'get_shipping_phone' ] ) ? wc()->customer->get_shipping_phone() : wc()->customer->get_meta( 'shipping_phone', true );
if ( is_callable( [ $order, 'set_shipping_phone' ] ) ) {
$order->set_shipping_phone( $shipping_phone_value );
} else {
$order->update_meta_data( '_shipping_phone', $shipping_phone_value );
}
}
}

View File

@ -188,16 +188,13 @@ class Cart extends ControllerTestCase {
$action_callback = \Mockery::mock( 'ActionCallback' );
$action_callback->shouldReceive( 'do_customer_callback' )->once();
$action_callback->shouldReceive( 'do_order_callback' )->once();
add_action( 'woocommerce_blocks_cart_update_customer_from_request', array( $action_callback, 'do_customer_callback' ) );
add_action( 'woocommerce_blocks_cart_update_order_from_customer_request', array( $action_callback, 'do_order_callback' ) );
$response = rest_get_server()->dispatch( $request );
$data = $response->get_data();
remove_action( 'woocommerce_blocks_cart_update_customer_from_request', array( $action_callback, 'do_customer_callback' ) );
remove_action( 'woocommerce_blocks_cart_update_order_from_customer_request', array( $action_callback, 'do_order_callback' ) );
$this->assertEquals( 200, $response->get_status(), print_r( $response, true ) );
$this->assertArrayHasKey( 'shipping_rates', $data );