I'm looking to reorder the product table on the Cart page in WooCommerce on WordPress. Currently the products listed go from oldest - newest (from order of adding to cart) and want to have the opposite, looking to have most recent added on top and oldest on bottom.
do_action( 'woocommerce_before_cart' ); ?>
<div class="cart_container">
<form class="cart-form" action="<?php echo esc_url( WC()->cart->get_cart_url() ); ?>" method="post">
<?php do_action( 'woocommerce_before_cart_table' ); ?>
Would it be possible to add orderby when calling the cart_url?
To do any kind of cart ordering you have to use
woocommerce_cart_loaded_from_session hook; and to reverse the
order simply use PHP array_reverse function.
Here is the code:
add_action('woocommerce_cart_loaded_from_session', 'wh_cartOrderItemsbyNewest');
function wh_cartOrderItemsbyNewest() {
//if the cart is empty do nothing
if (WC()->cart->get_cart_contents_count() == 0) {
return;
}
//array to collect cart items
$cart_sort = [];
//add cart item inside the array
foreach (WC()->cart->get_cart() as $cart_item_key => $cart_item) {
$cart_sort[$cart_item_key] = WC()->cart->cart_contents[$cart_item_key];
}
//replace the cart contents with in the reverse order
WC()->cart->cart_contents = array_reverse($cart_sort);
}
Code goes in function.php file of your active child theme (or theme). Or also in any plugin php files.
Code is tested and works.
Hope this helps!
The accepted answer has one major flaw: it creates a race condition and an infinite AJAX refresh loop with multiple tabs open (see here).
The way I was able to get around this is using action hooks:
Before the cart contents are looped through, we reverse the contents and save the new, reversed order
After the cart contents are looped through, we repeat step 1 to restore the original order
There are three areas (by default) where the cart items are looped on the frontend, so the action hooks I use cover each of those areas.
Here's the tested code:
function reverse_cart_contents() {
$cart_contents = WC()->cart->get_cart_contents();
if($cart_contents) {
$reversed_contents = array_reverse($cart_contents);
WC()->cart->set_cart_contents($reversed_contents);
}
}
add_action('woocommerce_before_mini_cart', 'reverse_cart_contents');
add_action('woocommerce_after_mini_cart', 'reverse_cart_contents');
add_action('woocommerce_before_cart', 'reverse_cart_contents');
add_action('woocommerce_after_cart', 'reverse_cart_contents');
add_action('woocommerce_review_order_before_cart_contents', 'reverse_cart_contents');
add_action('woocommerce_review_order_after_cart_contents', 'reverse_cart_contents');
You can modify woocommerce plugin's cart/cart.php template file. When loop starts with "WC()->cart->get_cart()" on cart page, you can first take this array into a separate one, reverse and then use this reversed array for showing cart products in reverse order.
This option is suggested because you don't actually interact with woocommerce object and hence it involves lesser processing. You're just showing them upside down.
Related
I've been looking for a solution to disable certain products from the same category if one of the items is added to the cart.
For example: I have 1 category with products from A to E.
If I add product A to the cart I want to disable products B and E, so they can't be added to the cart during the same shopping session.
Is there a way to achieve this?
Many thanks.
Yes , you actually have two options ,one is to customize your own plugin and insert it to your website but this actually takes time .The other way is to download a wholesale and b2b plugin , these plugins usually contain features as the one you are looking for , so you will be able to activate the feature that you want .
You can do that by validating the cart whenever an item is added to it.
You can do that by editing your theme (or child theme) functions.php file or by using your custom plugin.
You can use the following hook 'woocommerce_add_to_cart_validation'
add_filter( 'woocommerce_add_to_cart_validation', 'allowed_item_category_in_the_cart', 10, 2 );
function allowed_item_category_in_the_cart( $passed, $product_id) {
$product = wc_get_product($product_id);
// Get product categories
$product_category_ids = $product->get_category_ids();
// Then you iterate through each cart item
foreach (WC()->cart->get_cart() as $cart_item_key=>$cart_item ){
$cart_product_id = $cart_item['product_id'];
$cart_product = wc_get_product($cart_product_id);
$cart_product_category_ids = $cart_product->get_category_ids();
if (array_intersect($cart_product_category_ids, $product_category_ids))
return false;
}
return true;
}
PS: I just wrote this code. I didn't have time to test it yet.
Let me know your feedback.
I want to limit the number of items a user can have in cart to a maximum of 1 item (regardless the quantity).
There are several answers about how to achieve this using 'woocommerce_add_to_cart_validation' & 'woocommerce_update_cart_validation' filters (e.g. answer 1, answer 2, answer 3, ...).
It's also possible to use existing plugins like this one but it doesn't support maximum number of items limit.
However, in my case, both filters didn't fire due to the customization to 'Add to cart' logic that is done by the theme I'm using (for business/technical reasons that are irrelevant to discuss here).
That said, I need to achieve the same functionality using a different solution (using another filters, ...).
To achieve the required functionality, this can be done by using 'woocommerce_add_cart_item' filter as follows:
add_filter( 'woocommerce_add_cart_item' , 'validate_cart_max_items');
function validate_cart_max_items( $item_data ) {
// Check total cart quantity
$cart_contents_count = WC()->cart->get_cart_contents_count();
// If quantity > 0, cancel current item addition to cart & display an error message
if($cart_contents_count > 0) {
$item_data = NULL;
wc_add_notice( "A maximum of 1 item can be added to your cart", "error" );
}
return $item_data;
}
The above code snippet should be added to functions.php file under your active theme folder.
This code snippet has been tested using WordPress v5.3.1 and WooCommerce v3.8.1
I am trying to AJAXify my Woocommerce cart but I am failing hard and I really can't find any help or documentation (I searched for days). So any help is desperately appreciated.
I tried two approaches:
Custom AJAX Function
When changing the quantity (on the cart page), a AJAX call is fired (-> admin-ajax.php) which triggers a function in my functions.php:
function setQty() {
global $woocommerce;
WC()->cart->set_quantity( $_POST['itemKey'], $_POST['quantity'], true);
echo json_encode(array('totalCount'=>WC()->cart->get_cart_contents_count(), 'total'=>$woocommerce->cart->total)); }
The echoed JSON String contains the correct number of items in cart but the total amount is 0. When I reload the page the altered quantity is not saved.
Same with
WC()->cart->add_to_cart($product_id, $quantity) or adding WC()->cart->calculate_totals()
I tried it this way so i can format the values I need to update my cart how i want.
What am I missing here in order to get the cart update saved?
The woocommerce built-in AJAX update function doesn't trigger, because I have a custom design and I think it needs a specific HTML-structure in order to work properly but i can't find any documentation or examples. Also I need the updated values (total amount, total count etc.) in a custom format. So far it just passes me "updated fragments" as predefined html code.
How do i need to build the HTML code of my cart.php that the ajax update will work? And how do i alter the returning values?
Thank you very much in advance.
I'm not sure what you'd like to update, but I guess you want to update the totals amount of products in the basket.
This example replaces and updates the HTML
Let's say you have a div where your total amount is in:
<div class="cart-totals"><?php echo WC()->cart->get_cart_contents_count(); ?></div>
Add this to your functions.php:
// Mini Cart update with AJAX
add_filter( 'woocommerce_add_to_cart_fragments', 'custom_cart_count_fragments', 10, 1 );
function custom_cart_count_fragments( $fragments ) {
$fragments['div.cart-totals'] = '<div class="cart-totals">' . WC()->cart->get_cart_contents_count() . '</div>';
return $fragments;
}
Note: You are replacing not adding
Is it a mini cart/counter you are trying to create or a full page to review/remove products?
If it is a mini-cart/cart counter you wanted, I've created a repo under https://github.com/samisonline/ajax_cart for you that has the basic functionality baked in. This may not be the exact format you're looking for, but it shows the woocommerce functionality at work, as well as the needed markup for AJAX to work!
I'm trying to add a product to a woocommerce cart with a wp_loaded action after I submit a custom add to cart form.
add_action( 'wp_loaded', 'custom_process_form' );
function custom_process_form(){
global $woocommerce;
if(isset($_POST["addcoupon"])){
foreach($_POST as $key=>$value){
if($key=="addcoupon"){
continue;
}
$valarr=explode("_",$key);
if ($valarr[0]=="couponid"){
$woocommerce->cart->add_to_cart($valarr[1],1);
$count++;
}
}
}
}
It works fine except when it first loads, the cart thinks that I added 2 products instead of 1. For some reason, its running WC_Cart->calculate_totals() twice and $this->cart_contents_count is not reset to zero, so the quantity gets twice. (When I reload the page, the cart shows the correct number of items)
What is the correct way to add a product with a custom form? I can't find any examples of this. Am I using the wrong action?
I experimented on my own install and can confirm the same is happening to me. For me calculate_totals() is getting called once by the add to cart process and then again by the WooCommerce Subscriptions plugin getting the cart from the session. Do you have this plugin installed?
That said I don't think calling calculate_totals() is really a problem. The real problem is that $this->cart_contents_count should reset. I got around this by adding this code to functions.php.
function reset_quantities( $cart )
{
$cart->cart_contents_count = 0;
}
add_action( 'woocommerce_before_calculate_totals', 'reset_quantities' );
EDIT: Actually that doesn't work for me. The reason is that the WooCommerce Subscriptions plugin is calling calculate_totals() through the 'woocommerce_before_calculate_totals' action. This prevents other functions on the same action from firing. This is mentioned at this ticket. https://core.trac.wordpress.org/ticket/17817
Try this instead
function reset_quantities( $cart_object )
{
$cart = $cart_object->get_cart();
$cart_object->cart_contents_count = 0;
foreach ( $cart as $cart_item_key => $values ) {
$cart_object->cart_contents_count += $values['quantity'];
}
}
add_action( 'woocommerce_after_calculate_totals', 'reset_quantities', 10, 1 );
EDIT2 I just looked at the latest commit of WooCommerce on Github and calculate_totals() has changed a lot. You may want to try downloading that to see if it solves your problem.
I have two distinct products in a WooCommerce cart. One is a ticket, the other is a fee that must be paid before the user can upload a file (which is being handled by a Gravity Form I've created).
Currently, if I put the link to the page with the Gravity Form on the Order Received page, if someone purchases just a ticket, they would see this link and it would be confusing.
Is there a way to either have unique confirmation pages once a purchase is complete based on the product purchased?
If not, are there conditional tags or some kind of hook or filter that would only show the link to the Gravity Form on the Order Received page if the "fee product" is purchased (possibly based on product ID or category ID)?
I did find this code snippet:
https://sozot.com/how-to-hook-into-woocommerce-to-trigger-something-after-an-order-is-placed/
But when I try this:
add_action('woocommerce_payment_complete', 'custom_process_order', 10, 1);
function custom_process_order($order_id) {
$order = new WC_Order( $order_id );
$myuser_id = (int)$order->user_id;
$user_info = get_userdata($myuser_id);
$items = $order->get_items();
foreach ($items as $item) {
if ($item['product_id']==154) {
echo '<h3>If you are submitting a script, please click here to submit your script.</h3>';
}
}
return $order_id;
}
Nothing gets displayed on the Order Details screen. Oddly enough, if I try and use wp_redirect and force a redirect the page, it "works", but it breaks the page and causes some strange embedding of the site within the checkout page.
After beating on this incessantly, I finally found what I was looking for, so I thought I'd post it. In my case, I wanted to customize the Order Detail/Confirmation page with a link to a form, but only when a certain product is purchased.
If you want something similar, put this in the order_details.php template:
global $woocommerce;
$order = new WC_Order( $order_id );
/* This two lines above should already exist, but I have them here so you can
see where to put the code */
foreach($order->get_items() as $item) {
$_product = get_product($item['product_id']);
if ($item['product_id']==154) {
// Do what you want here..replace the product ID with your product ID
}
}
I also tested this by inserting a wp_redirect within the if statement and it seemed to work great, so I imagine you could customize this code with a bunch of if/elseif statements that redirect to different landing pages depending on the product purchased! Hopefully this helps someone!