14

I have an item with the following data :

var item = {
  id : "124",
  name : "xxx",
  price : "13.13",
  quantity : 1,
  options : {
    "size" : "xl",
    "color": "pink"
  }
};

When the user clicks on "Add to Cart" I'd like to make an Ajax Request using the WC API and add the above item to the Cart.

jQuery.ajax({
   url: "some/woocommerce/api/add/to/cart/request/path",
   data: item,
   type: "POST"
});

Then on the Cart Page I'd like to make another Ajax Request using the WC API and retrieve the contents of the cart.

I haven't found any documentation (official or unofficial) on how to do that from the client using Javascript.

Does anyone know and can provide me with an example please?

Also, does anyone know why the WooCommerce Api Documentation is so horrible (lacking any kind of information regarding obvious/standard questions like the above). I'm seriously thinking of having our company switch to Shopify.

helgatheviking
  • 25,596
  • 11
  • 95
  • 152
Kawd
  • 4,122
  • 10
  • 37
  • 68
  • Are you trying to do this from the same domain? I am reading through the API-related files and I don't see anything designed for managing a shopping cart. However, if you are on the same domain, WC already does add to cart via AJAX which you can look at and borrow. – helgatheviking Dec 03 '14 at 13:34
  • Yes I will be doing this from the same domain. Where can I find this AJAX request you're referring to. I am not using any WC generated pages. Just a blank Wordpress HTML page that loads on the same domain where WC is installed. – Kawd Dec 03 '14 at 16:44
  • Won't you need at least the cart and order pages so people can check out? Anyway, it was too long for a comment, so I posted what I know about the ajax add to cart callback below. – helgatheviking Dec 03 '14 at 17:36

2 Answers2

31

You can investigate how WooCommerce is adding items to the cart via ajax directly in the code.... the callback is located in includes/class-wc-ajax.php. WooCommerce already does this on product "loops" (product archives), so I don't think you need to reinvent the wheel and if their existing code doesn't work for what you are trying to do, then you should be able to borrow heavily from it.

The beginning of that file has a loop with all the WooCommerce Ajax actions, but we can track down that the add to cart is basically this:

add_action( 'wp_ajax_nopriv_woocommerce_add_to_cart', array( 'WC_AJAX', 'add_to_cart' ) );

And it's callback is a little further down the file:

/**
 * AJAX add to cart
 */
public static function add_to_cart() {
    ob_start();

    $product_id        = apply_filters( 'woocommerce_add_to_cart_product_id', absint( $_POST['product_id'] ) );
    $quantity          = empty( $_POST['quantity'] ) ? 1 : wc_stock_amount( $_POST['quantity'] );
    $passed_validation = apply_filters( 'woocommerce_add_to_cart_validation', true, $product_id, $quantity, $variation_id, $variations, $cart_item_data );

    if ( $passed_validation && WC()->cart->add_to_cart( $product_id, $quantity ) ) {

        do_action( 'woocommerce_ajax_added_to_cart', $product_id );

        if ( get_option( 'woocommerce_cart_redirect_after_add' ) == 'yes' ) {
            wc_add_to_cart_message( $product_id );
        }

        // Return fragments
        self::get_refreshed_fragments();

    } else {

        // If there was an error adding to the cart, redirect to the product page to show any errors
        $data = array(
            'error' => true,
            'product_url' => apply_filters( 'woocommerce_cart_redirect_after_error', get_permalink( $product_id ), $product_id )
        );

        wp_send_json( $data );

    }

    die();
}

If you want to view their add to cart ajax call, the JS for that is located in assest/js/frontend/add-to-cart.js.

EDIT

Now that I know you are looking to add a variation, maybe we can tweak the above.

First, I think you'll need to pass the AJAX url to your script:

wp_enqueue_script( 'wc-variation-add-to-cart', 'source-to-script/your-script.js' );

$vars = array( 'ajax_url' => admin_url( 'admin-ajax.php' ) );
wp_localize_script( 'wc-variation-add-to-cart', 'WC_VARIATION_ADD_TO_CART', $vars );

Then your AJAX call would look something like this:

jQuery.ajax({
    url: WC_VARIATION_ADD_TO_CART.ajax_url,
    data: {
        "action" : "woocommerce_add_variation_to_cart",
        "product_id" : "124",
        "variation_id" : "125",
        "quantity" : 1,
        "variation" : {
            "size" : "xl",
            "color": "pink"
        },
    },
    type: "POST"
});

Basically to add a specific variation you need the variation's ID in addition to all its specific options.

And finally the new callback for the woocommerce_add_variation_to_cart ajax action would be along the lines of the following:

add_action( 'wp_ajax_nopriv_woocommerce_add_variation_to_cart', 'so_27270880_add_variation_to_cart' );

function so_27270880_add_variation_to_cart() {

    ob_start();

    $product_id        = apply_filters( 'woocommerce_add_to_cart_product_id', absint( $_POST['product_id'] ) );
    $quantity          = empty( $_POST['quantity'] ) ? 1 : wc_stock_amount( $_POST['quantity'] );

    $variation_id      = isset( $_POST['variation_id'] ) ? absint( $_POST['variation_id'] ) : '';
    $variations         = ! empty( $_POST['variation'] ) ? (array) $_POST['variation'] : '';

    $passed_validation = apply_filters( 'woocommerce_add_to_cart_validation', true, $product_id, $quantity, $variation_id, $variations, $cart_item_data );

    if ( $passed_validation && WC()->cart->add_to_cart( $product_id, $quantity, $variation_id, $variations ) ) {

        do_action( 'woocommerce_ajax_added_to_cart', $product_id );

        if ( get_option( 'woocommerce_cart_redirect_after_add' ) == 'yes' ) {
            wc_add_to_cart_message( $product_id );
        }

        // Return fragments
        WC_AJAX::get_refreshed_fragments();

    } else {

        // If there was an error adding to the cart, redirect to the product page to show any errors
        $data = array(
            'error' => true,
            'product_url' => apply_filters( 'woocommerce_cart_redirect_after_error', get_permalink( $product_id ), $product_id )
        );

        wp_send_json( $data );

    }

    die();
}

Mostly, I'm just copying WooCommerce's approach and adding in the 2 variables needed for adding variations. Totally untested, but I hope it helps.

jayrchamp
  • 85
  • 1
  • 9
helgatheviking
  • 25,596
  • 11
  • 95
  • 152
  • Thanks for that. I'm having 2 problems. The 1st one is that I don't know PHP so looking at the server-side code I don't really understand how to translate that into client-side Javascript. – Kawd Dec 03 '14 at 19:57
  • My 2nd problem is that the above code (and variations of it which I've found online) basically only adds a product ID and a product quantity to the cart. That item-object (that will be generated the moment the user clicks on add-to-cart and after having selected various item-options) that I want to add to the basket looks something like this : `{name: "", id: "", price: "", photo: "", quantity: "", options: {size: "XL", color: "pink", attributeX: "someOtherValue", etc..}}`. – Kawd Dec 03 '14 at 19:58
  • So just adding a product ID and its quantity isn't enough in my case as each object comes with custom options which I've defined from within the WC UI over at the Wordpress Dashboard. – Kawd Dec 03 '14 at 19:58
  • Ok, so then you are talking about variable products. WooCommerce doesn't have anything built-in for adding those to the cart via ajax, probably because the options need to be configured first on the product page. I do think this is possible. You'll have to use the `add_to_cart()` action (is mentioned in the code block I already posted and can be found in the `WC_Cart` class. That function has 5 parameters, 2 of which specifically concern variations. – helgatheviking Dec 03 '14 at 20:38
  • Hi there and thanks for getting back to me so quickly. I'm not talking about variable products. All the products across the shop will be "simple" products with multiple options. According to the WC UI I'm allowed to do that. The user will be able to choose different options for a product but this will not affect the price of the product so these won't be "variable" products with variable prices. Imagine a pair of shorts with a fixed price but with the options to choose size and color. Given that the WC UI allows me to create "simple" products with multiple options it looks like I can do that. – Kawd Dec 03 '14 at 20:58
  • When you talk about using the add_to_cart() action for example, I really don't understand how I am supposed to use that client-side via an Ajax call. The example of an Ajax call that I have in my OP is all I can basically perform on the client. There are no WC functions available for me in the window to call. So how do I combine what's in the PHP code you have provided with an Ajax call ont he client? e.g.What should my Ajax Request URL be? What about the data format containing all the relevant item details or the query parameters syntax in my Request URL?This is what I'm having trouble with.. – Kawd Dec 03 '14 at 21:03
  • But that is exactly what a variable product *is*. A variable product is just a product with options. They don't necessarily have to have different prices, though they can. – helgatheviking Dec 03 '14 at 21:05
  • Using the PHP callbacks for AJAX actions can be tricky. I would suggest you start by reading [AJAX in Plugins](http://codex.wordpress.org/AJAX_in_Plugins). To simplify (as best I can) all AJAX calls go to WordPress's `admin-ajax.php` URL. From there is looks for a function that is attached to hook with a predefined name. For admin-side calls this is `wp_ajax_$action_name` and for front-end calls it would be `wp_ajax_no_priv_$action_name`... where the `$action_name` was defined in the javascript. – helgatheviking Dec 03 '14 at 21:07
  • Oh I see (also I didn't realize you had edited your post while I was typing in my previous comments..sorry!). OK I guess I got confused because like I said, in the WC UI, when creating a product there is a dropdown where you can select either a "simple" or a "variable" product. When selecting a "simple" product you then still get the option to add "options" to your "simple" product, as in option "flavor" with values : "chocolate"|"strawberry"|"lemon"..so I was confused. Anyway, so looking into what you suggested is a good starting point! – Kawd Dec 03 '14 at 21:12
  • And if I understand correctly I will basically have to manually edit some .php files in order to "prepare" them for my client-side Ajax calls or something along these lines. Like I said I don't know PHP but I guess I will have to start learning if I want to make this work. Thanks once again for your help! :-) – Kawd Dec 03 '14 at 21:14
  • 1
    I hope all that helps some. But yes, if you want to work with WooCommerce you will have to learn some PHP. It sounds like you will want to [create a plugin](http://wpninjas.com/how-to-create-a-simple-wordpress-plugin/). – helgatheviking Dec 03 '14 at 21:23
  • Thanks for `WC_AJAX::get_refreshed_fragments()`, as this totally pointed me in the right direction (although with removing rather than adding items in the minicart via Ajax). Since I threw it into my own ajaxified function, it returned `fragments` as an object and I was able to loop through and replace DOM elements on the fly (and thus update the minicart), even after extending the fragments with `add_filter('add_to_cart_fragments')`. – indextwo Dec 31 '14 at 17:26
  • I don't believe that a big plugin like WooCommerce has this horrible documentation, such simply add/edit product via ajax API (that is the problem where i'm looking for) looks like an hard task to achieve. However I ask if are already found official / unofficial documentation that could be useful in cases like these. – and.ryx Jul 10 '15 at 11:59
  • This pretty much got me up and running. Fantastic. The only issue I'm running into is that when running through the get_refreshed_fragments action (i.e. after I add to cart) it removes the plus/minus steps aside the quantity value. If I simply refresh the browser (i.e. get_refreshed_fragments not called) they return. Any idea? – RyanMac Jul 28 '15 at 07:18
  • Not really. But you should make that into a separate question. – helgatheviking Jul 28 '15 at 11:05
  • 1
    I think we should pass to the woocommerce_add_to_cart_validation filter the $variation_id and $variation variables. (Reference: https://docs.woothemes.com/wc-apidocs/source-class-WC_Form_Handler.html#650) – Laxmana Oct 10 '15 at 12:13
  • You don’t have to necessarily send the variation object since you can get it by id - see http://stackoverflow.com/a/22817556/601466 – borisdiakur Apr 28 '16 at 20:14
  • @borisdiakur I have read on https://github.com/woocommerce/woocommerce/issues/10810 you have accomplished to add a variable product to the cart with AJAX. Would you like to show me some code how you figured it out? –  Feb 26 '17 at 00:25
  • @Swolschblauw This should help: https://github.com/woocommerce/woocommerce/issues/10810#issuecomment-215563191 – borisdiakur Feb 26 '17 at 23:11
  • If you send a product ID and variation ID from the JS script, then my above code *should* work. If you are having a specific problem then perhaps create a new question with all the relevant details. – helgatheviking Feb 27 '17 at 00:52
  • Thx dude! You saved alot of time for me :) – Sergei Zahharenko May 27 '21 at 01:43
5

If anyone is interested I've also written a "remove from cart" function to counter the add to cart. My project called for radio buttons on checkout so I needed to add a product and remove the alternative it was already there:

Here's the php:

function remove_from_cart( $product_id ) {
    $cart_contents  = WC()->cart->get_cart();

    if (empty($product_id)) { $product_id = $_POST['product_id']; }

    foreach ($cart_contents as $product_key => $product){
        if ($product['variation_id'] == $product_id){
            WC()->cart->remove_cart_item($product_key);
        }
    }
}

add_action( 'wp_ajax_nopriv_remove_from_cart', 'remove_from_cart' );

And then here's the jQuery:

function remove_item_from_cart(product){

    var data = {
                "action" : "remove_from_cart",
                "product_id" : product,
    };

    jQuery.post(WC_VARIATION_ADD_TO_CART.ajax_url, data, function(response) {
        $(document.body).trigger("update_checkout");
    });

}

The if(empty)... in the php was so I could call the function from the server side as well if I needed to.

Termininja
  • 6,620
  • 12
  • 48
  • 49
RestlessWeb
  • 143
  • 1
  • 8