Spread the word

Managing the cart using the REST API after WooCommerce 3.6

August 7, 2020 17 comments

Since WooCommerce 3.6, some front-end functions and classes are no longer loaded for REST API requests. Read on to see how to load these manually in your theme or plugin.

WooCommerce 3.6 (released 7 April 2019) included some fairly significant changes from a development point of view. One of the performance improvements was to remove the cart and other frontend code for REST API requests. From 3.6 they are only loaded for ‘frontend’ requests, i.e. those displaying an actual page on your website.

This is a worthwhile improvement but there there is a downside: if you have a plugin or some custom code which interacts with the cart or frontend code via the REST API, you’ll suddenly find it no longer works.

Our WooCommerce Quick View Pro plugin was affected by this problem. In that plugin, we allow products to be added to the cart from a lightbox, and we use the REST API to facilitate that. Without wishing to rewrite the plugin to use an alternate method (e.g. AJAX) we found a workaround for this, which may be helpful to other plugin developers.

First some good news: the WooCommerce autoloader takes care of including any classes referenced during your REST request. So even though the cart class is no longer included upfront, if you reference WC_Cart in your code, then the autoloader will load it for you.

If you use functions such as wc_add_to_cart_message, you’ll need to include the cart functions separately (/woocommerce/includes/wc-cart-functions.php) as this file won’t be autoloaded. And since the cart often creates notices (e.g. adding items to the cart), you’ll probably want to include those functions too:

include_once WC_ABSPATH . 'includes/wc-cart-functions.php';
include_once WC_ABSPATH . 'includes/wc-notice-functions.php';

You may also need the template hooks file, if your request outputs one of the WooCommerce templates:

include_once WC_ABSPATH . 'includes/wc-template-hooks.php';

Next, we need to initialise the session class:

if ( null === WC()->session ) {
    $session_class = apply_filters( 'woocommerce_session_handler', 'WC_Session_Handler' );
    WC()->session = new $session_class();
    WC()->session->init();
}

This code is basically a straight copy of what WooCommerce itself does during WooCommerce->init().

Next, we need to initialise the customer and cart objects, as these are no longer created for REST requests:

if ( null === WC()->customer ) {
    WC()->customer = new WC_Customer( get_current_user_id(), true );
}
if ( null === WC()->cart ) {
    WC()->cart = new WC_Cart();
}

Finally, we need to load the existing cart from the session. Even though we’ve created the cart at this point, the cart contents will be empty.

The cart storage is handled by the WC_Cart_Session class. If you look at the code, you’ll see that the initial read of the cart is done on the wp_loaded hook. As we are executing a REST request which runs during parse_request, this hook will have already happened.

So we need to force the cart to refresh its contents from the session. The simplest way to do that is to call the get_cart() method:

WC()->cart->get_cart();

That’s pretty much it. In our quick view plugin, we put this together inside a check_prerequisites() function which runs right at the start of our REST request. Here’s the code we use:

/**
 * Check any prerequisites for our REST request.
 */
private function check_prerequisites() {
	if ( defined( 'WC_ABSPATH' ) ) {
		// WC 3.6+ - Cart and other frontend functions are not included for REST requests.
		include_once WC_ABSPATH . 'includes/wc-cart-functions.php';
		include_once WC_ABSPATH . 'includes/wc-notice-functions.php';
                include_once WC_ABSPATH . 'includes/wc-template-hooks.php';
	}

	if ( null === WC()->session ) {
		$session_class = apply_filters( 'woocommerce_session_handler', 'WC_Session_Handler' );

		WC()->session = new $session_class();
		WC()->session->init();
	}

	if ( null === WC()->customer ) {
		WC()->customer = new WC_Customer( get_current_user_id(), true );
	}

	if ( null === WC()->cart ) {
		WC()->cart = new WC_Cart();

		// We need to force a refresh of the cart contents from session here (cart contents are normally refreshed on wp_loaded, which has already happened by this point).
		WC()->cart->get_cart();
	}
}

I hope you find this useful. I’ll try keep this article up to date as new versions of WooCommerce are released. Let me know if you spot any problems or have any improvements to the code in the comments below.

Andy Keith

Technical Director @Barn2 Plugins. With over 15 years in the industry, Andy is a WordPress and software developer with a keen interest in object-oriented design and programming.

17 Comments

  1. Jaswinder Singh
    July 14, 2020 Reply

    Its giving me an empty array
    print_r(WC()->cart);die; //Empty keys in array
    print_r(WC()->cart->get_cart(););die; //Empty array

    • Andy Keith
      July 14, 2020 Reply

      Was your cart empty before doing the REST request?

  2. Bogdan
    June 9, 2020 Reply

    Hey Andy. Thank you, thank you, thank you! Same as Dylan above, your article helped me so much. My plugin has a REST API request that creates an order with subscription product, which uses virtual WC_Cart to process the subscription properly. And was having a nasty error "Call to a member function get() on null in /.../woocommerce/includes/class-wc-cart-session.php on line 73" obviously connected to WC Session not being initialized. And this article helped me realize what I was missing.

  3. Dylan P
    November 13, 2019 Reply

    Thanks for explaining this so concisely, your article helped me immensely!

Please share your thoughts...

Your email address will not be published.