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
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'm figuring out a filter but can't be figured :)
Just want to change PrimaryImageOfPage value with an og:image value in CollectionPage for Taxonomies.
It may be that I have to use woocommerce_structured_data_product to filter here and look for the image $markup['image'] = $image;
I have created some filters, but it isn't woocommerce_structured_data_product I guess.
One of them was like that but it... nah.
add_filter( 'woocommerce_structured_data_product', function( $markup ) {
$markup['image'] = ('rank_math/opengraph/{$network}/image');
return $markup;
});
What am I doing wrong? I have been doing this whole day -_-
If You inspect the code of the class WC_Structured_Data, You would see that woocommerce_structured_data_product filter is executed inside function generate_product_data
WC Complete Source of the class WC_Structured_Data : https://github.com/woocommerce/woocommerce/blob/b88b868ab8919b7c854173098b7d6d4ab227f9ee/includes/class-wc-structured-data.php
$this->set_data( apply_filters( 'woocommerce_structured_data_product', $markup, $product ) );
And generate_product_data function is executed on action woocommerce_single_product_summary
add_action( 'woocommerce_single_product_summary', array( $this, 'generate_product_data' ), 60 );
So, your code would work on product single page only and not archive/list pages, such as the taxonomy page.
The feature you want to achieve is not feasible out of the box in WC, You will need to customize yourself.
Logically, structured data on a single product page is feasible due to the fact there is only one product and product information is available, on the other hand, product list has more than one product (you might need to consider like you want to show details of first product) so it is ambiguous and not feasible out of the box for WC.
I have a unique issue. I am using a plugin where they are not able to support the request. I need to split out variations into separate items, but if I copy and paste and turn them into a simple product, then I can't sync the count for the product to track inventory stock. As a workaround I needed to be able to disable the variations I do not need, keeping only the one that I do need. But here is where I am having trouble. If I have one variation enabled, then I do not want to show the dropdown, and instead want it to look like a simple product on the UI. I tried everything and cannot get it to work.
I even tried using
add_filter( 'woocommerce_hide_invisible_variations', '__return_true', 10, 3 );
with no success as they are visible and not hidden even though the counts are 0, the price is 0, and the item is disabled.
How can I show the product page with no drop-down? Take it one step further; I delete all variations except the one that I need to keep. I need to keep it in variations mode due to the plugin that syncs. How do I display it without any dropdowns showing?
Example logic:
If product type is a variation and enabled count == 1 then special ui display, else normal.
Thanks.
IMPORTANT: The code only works when the unique variation is selected as default form value:
The following code will hide from variable products that have only one variation enabled and selected by default, the attribute dropdown and the selected variation price:
add_action( 'woocommerce_before_add_to_cart_form', 'single_unique_variation_ui', 10 );
function single_unique_variation_ui(){
global $product;
if( ! $product->is_type('variable') )
return; // Only variable products
$available_variations = $product->get_available_variations(); // Get available variations
$default_attributes = $product->get_default_attributes(); // Get default attributes
// Only for a unique selected variation by default
if( ! ( sizeof($available_variations) == 1 && sizeof($default_attributes) == 1 ) )
return;
// Get the unique variation
$variation = reset($available_variations);
// Loop through
if( reset($variation['attributes']) == reset($default_attributes) ) :
// Styles
?>
<style>
div.woocommerce-variation-price, table.variations { display:none; }
</style>
<?php
endif;
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.
Without the code (the normal woocommerce behavior):
With the code (that hide the attribute dropdown and the price):
It will give you the same UI than simple products
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.
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!