I amusing WooCommerce and with WooCommerce Bookings plugin. In their documentation there's this solution for booking multiple items by using the "persons" quantity.
So what the Bookings plugins does, it ads meta data to the variations, it also includes "persons" what we're currently using as an indicator for the amount of items being booked.
What we want to next is retrieve the meta data [Persons] from the product variations and display this as the item quantity.
What I've done so far is edit the woocommerce cart template and copied it to our child theme. (we can't use hooks since there's no hook available to use in the shop table).
</td>
<!-- Use [_persons] as product quantity -->
<td class="product-quantity--booking">
<?php
if ( ! empty( $cart_item['booking'] ) ) {
echo WC()->cart->get_item_data( $cart_item );
}
?>
</td>
What I need to figure out is, how do i filter these variations and only show the meta data I need? I would only need to show meta data "persons".
The next thing would be to filter only the start date and end date to display on the other meta data column.
[EDIT 18/02]
So I've found out that I can't use "get_item_date" since it does not take any arguments to show a specific value.
I tried to use "wc_display_item_meta", but I don't know how to select the item I need.
<!-- Use ['Persons'] as product quantity -->
<td class="product-quantity--booking">
<?php
if ( ! empty( $cart_item['booking'] ) ) {
$item_meta = wc_display_item_meta( $cart_item['booking']['Persons'])
echo $item_meta;
}
?>
</td>
By using the vardump I found the key I need is "['booking']['Persons']"
Maybe someone could help me to point out how to filter the array for needed items?
The wc_display_item_meta() function is not to be used in Cart or with cart items. It's explicitly for WC_Order_Item where $item argument is an Order Item.
You will see in the source code:
/**
* Display item meta data.
*
* #since 3.0.0
* #param WC_Order_Item $item Order Item.
* #param array $args Arguments.
* #return string|void
*/
function wc_display_item_meta( $item, $args = array() ) { /* … … */ }
So this function will not work in your case.
How to do it (2ways):
1) You can use two custom hooked functions, that will remove all booking data except 'Persons' and that will remove the quantity for your bookable products only.
// Display only 'Persons' for bookable products
add_filter( 'woocommerce_get_item_data', 'display_only_persons_for_bookings', 20, 2 );
function display_only_persons_for_bookings( $cart_data, $cart_item ){
// Check that is a bookable product and that is Cart page
if( ! isset($cart_item['booking']) || ! is_cart() ) return $cart_data; // Exit
// Loop through this cart item data
foreach($cart_data as $key =>$values){
// Checking that there is some data and that is Cart page
if( ! isset($values['name']) || ! is_cart() ) return $cart_data; // Exit
// Removing ALL displayed data except "Persons"
if( $values['name'] != __('Persons','woocommerce') ){
unset($cart_data[$key]);
}
}
return $cart_data;
}
// Remove cart quantity for bookable products
add_filter( 'woocommerce_cart_item_quantity', 'remove_cart_quantity_for_bookings', 20, 3 );
function remove_cart_quantity_for_bookings( $product_quantity, $cart_item_key, $cart_item ){
// Check that is a bookable product
if( isset($cart_item['booking']) )
$product_quantity = '';
return $product_quantity;
}
This code goes on function.php file of your active child theme (or theme).
Tested and works.
2) Or you can remove all booking displayed meta data and display the "Persons" in the quantity field:
// Remove displayed cart item meta data for bookable products
add_filter( 'woocommerce_get_item_data', 'remove_cart_data_for_bookings', 20, 2 );
function remove_cart_data_for_bookings( $cart_data, $cart_item ){
// Check that is a bookable product and that is Cart page
if( ! isset($cart_item['booking']) || ! is_cart() ) return $cart_data; // Exit
// Loop through this cart item data
foreach($cart_data as $key =>$values){
// Removing ALL displayed data
unset($cart_data[$key]);
}
return $cart_data;
}
// Add "Persons" to replace cart quantity for bookable products
add_filter( 'woocommerce_cart_item_quantity', 'replace_cart_quantity_for_bookings', 20, 3 );
function replace_cart_quantity_for_bookings( $product_quantity, $cart_item_key, $cart_item ){
// Check that is a bookable product
if( isset($cart_item['booking']) ){
$product_quantity = '<span style="text-align: center; display:inline-block;">'.$cart_item['booking']['Persons'].'<br>
<small>(' . __('persons','woocommerce') . ')</small><span>';
}
return $product_quantity;
}
This code goes on function.php file of your active child theme (or theme).
Tested and works
If you manage only booking products, you could rename "Quantity" column label to "Persons" in the template cart/cart.php (overriding it via your active theme).
Related
I am using this code to make the product show boxes in cart but I just want it to show it on certain products. How can I put the product id?
function cw_change_product_quantity_display( $quantity ) {
$quantity .= ' Caja (s)';
return $quantity;
}
add_filter( 'woocommerce_get_quantity_html', 'cw_change_product_quantity_display' );
add_filter( 'woocommerce_cart_item_quantity', 'cw_change_product_quantity_display' );
The hook woocommerce_get_quantity_html is not defined in WooCommerce core, so maybe it's a custom hook added by a third party plugin, your theme or by yourself…
Now for the hook woocommerce_cart_item_quantity, there are 2 additional optional arguments missing from your code that will allow you to target a product ID like:
add_filter( 'woocommerce_cart_item_quantity', 'change_cart_item_displayed_quantity', 10, 3 );
function change_cart_item_displayed_quantity( $product_quantity, $cart_item_key, $cart_item ) {
// Here define your product ID(s) in the array
$product_ids = array( 37, 53 );
if( array_intersect( [ $cart_item['product_id'], $cart_item['variation_id'] ], $product_ids ) ) {
$product_quantity .= __(' Caja (s)');
}
return $product_quantity;
}
It should work for some defined product IDs only.
But as $product quantity is an input number field, your custom text will be display after this quantity field, which is strange.
I have a function on my Woocommerce website that enables customers to set a custom amount to pay for a specific product, based on a value I'm passing through the URL.
I'm using the woocommerce_before_calculate_totals hook, and up until I upgraded to WC 3.3.5, it was working fine. Now, when I run the code, the checkout initially shows the custom amount.
However, once the loader has finished updating, it resets the price to '0' (i.e. displaying £0.00 the checkout page's total fields).
Here's that code:
add_action( 'woocommerce_before_calculate_totals', 'pay_custom_amount', 99);
function pay_custom_amount() {
$payment_value = $_GET['amount'];
foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
if($cart_item['data']->id == 21 ){
$cart_item['data']->set_price($payment_value);
}
}
}
Well, colour me baffled. I've trawled Stack Overflow for solutions but can't see any similar problems. I see the hook runs multiple times but gather this is normal.
If anyone know what might be happening here, it would be great if you could share.
You can't get a price from an URL and set it in woocommerce_before_calculate_totals action hook. This needs to be done differently.
In the below code:
the first hooked function will get that "amount" from the URl and will set it (register it) in cart item object as custom data.
the 2nd hooked function will read that amount from custom cart item data and will set it as the new price.
Now your the target product ID in your code need to be the same ID that the added to cart product.
The code:
// Get the custom "amount" from URL and save it as custom data to the cart item
add_filter( 'woocommerce_add_cart_item_data', 'add_pack_data_to_cart_item_data', 20, 2 );
function add_pack_data_to_cart_item_data( $cart_item_data, $product_id ){
if( ! isset($_GET['amount']) )
return $cart_item_data;
$amount = esc_attr( $_GET['amount'] );
if( empty($amount) )
return $cart_item_data;
// Set the custom amount in cart object
$cart_item_data['custom_price'] = (float) $amount;
$cart_item_data['unique_key'] = md5( microtime() . rand() ); // Make each item unique
return $cart_item_data;
}
// Alter conditionally cart item price based on product ID and custom registered "amount"
add_action( 'woocommerce_before_calculate_totals', 'change_conditionally_cart_item_price', 30, 1 );
function change_conditionally_cart_item_price( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 )
return;
// HERE set your targeted product ID
$targeted_product_id = 21;
foreach ( $cart->get_cart() as $cart_item ) {
// Checking for the targeted product ID and the registered "amount" cart item custom data to set the new price
if($cart_item['data']->get_id() == $targeted_product_id && isset($cart_item['custom_price']) )
$cart_item['data']->set_price($cart_item['custom_price']);
}
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.
I am trying to set the functionality in Woocommerce on the Add to Cart button to only allow a particular product to be added once to the Cart.
Once a particular product has been added to cart the first time, the Add to Cart needs to be hidden.
In the cart I can have any number of products - just a max of quantity 1 for each product.
I was doing research and saw that I can use woocommerce_add_to_cart_validation hook. But have no idea how to start off.
How can I allow a particular product to be added once to the Cart?
Disable add to cart button if product is in cart using woocommerce_is_purchasable hook:
add_filter( 'woocommerce_is_purchasable', 'disable_add_to_cart_if_product_is_in_cart', 10, 2 );
function disable_add_to_cart_if_product_is_in_cart ( $is_purchasable, $product ){
// Loop through cart items checking if the product is already in cart
foreach ( WC()->cart->get_cart() as $cart_item ){
if( $cart_item['data']->get_id() == $product->get_id() ) {
return false;
}
}
return $is_purchasable;
}
Code goes in function.php file of your active child theme (or active theme). Tested and works (even for product variations in variable products).
Original answer:
Here is an example using woocommerce_add_to_cart_validation hook and that will do the trick (preventing add to cart action and displaying a custom notice when needed), and using a custom utility function that will remove quantity field for your specific defined product ID:
add_filter( 'woocommerce_add_to_cart_validation', 'limit_cart_items_from_category', 10, 3 );
function limit_cart_items_from_category ( $passed, $product_id, $quantity ){
// HERE define your product ID
$targeted_product_id = 37;
// Check quantity and display notice
if( $quantity > 1 && $targeted_product_id == $product_id ){
wc_add_notice( __('Only one item quantity allowed for this product', 'woocommerce' ), 'error' );
return false;
}
// Loop through cart items checking if the product is already in cart
foreach ( WC()->cart->get_cart() as $cart_item ){
if( $targeted_product_id == $product_id && $cart_item['data']->get_id() == $targeted_product_id ) {
wc_add_notice( __('This product is already in cart (only one item is allowed).', 'woocommerce' ), 'error' );
return false;
}
}
return $passed;
}
// Checking and removing quantity field for a specific product
add_filter( 'woocommerce_quantity_input_args', 'custom_quantity_input_args', 10, 2 );
function custom_quantity_input_args( $args, $product ) {
// HERE define your product ID
$targeted_product_id = 37;
if( $targeted_product_id == $product->get_id() )
$args['min_value'] = $args['max_value'] = $args['input_value'] = 1;
return $args;
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.
Based on Allow checkout only when a product of a mandatory category is in cart I tried to make my own code sample that Renders a notice and prevents checkout if the cart
only contains products in specific categories. It works on prevention and error notice however on adding other products, it still denies checkout.
/**
* Renders a notice and prevents checkout if the cart
* only contains products in a specific category
*/
//add_action( 'woocommerce_check_cart_items', 'brown_wc_prevent_checkout_for_category' );
function brown_wc_prevent_checkout_for_category() {
// set the slug of the category for which we disallow checkout
$categories = array('drinks','extra-accompaniments');
foreach ($categories as $category => $value) {
// get the product category
$product_cat = get_term_by( 'slug', $category, 'product_cat' );
// sanity check to prevent fatals if the term doesn't exist
if ( is_wp_error( $product_cat ) ) {
return;
}
// check if this category is the only thing in the cart
if ( brown_wc_is_category_alone_in_cart( $category ) ) {
// render a notice to explain why checkout is blocked
wc_add_notice( sprintf( 'Please choose atleast one bento.', $category ), 'error' );
}
}
}
/**
* Checks if a cart contains exclusively products in a given category
*
* #param string $category the slug of the product category
* #return bool - true if the cart only contains the given category
*/
function brown_wc_is_category_alone_in_cart( $category ) {
//When the cart is empty, remove warning message that I have set for when the snippet takes effect
if (!WC()->cart->is_empty()) {
// All the code
// check each cart item for our category
foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
// if a product is not in our category, bail out since we know the category is not alone
if ( ! has_term( $category, 'product_cat', $cart_item['data']->id ) ) {
return false;
}
}
// if we're here, all items in the cart are in our category
return true;
} else {
return false; // Assume you'd want false here, since the cart is empty
}
}
There is some mistakes in your code that avoid it to work successfully. Your code and requirements are quiet different, than my previous answer.
You will have to set your needed button URL and text for the notice, like the "bento" product category link and button name.
Here is the code:
// Conditional function that check if the non mandatory product categories are in all cart items
function has_not_mandatory_category(){
// DEFINE HERE the your non mandatory product categories (Ids, slugs or names)
$categories = array('drinks','extra-accompaniments');
// Iterrating each item in cart and detecting…
foreach ( WC()->cart->get_cart() as $cart_item ) {
$product_id = $cart_item['product_id']; // <== This is the right way (working for product variations too)
if ( ! has_term( $categories, 'product_cat', $product_id ) )
return false;
}
return true;
}
// Display a message and prevent checkout if the non mandatory product categories are not in all cart items
add_action( 'woocommerce_check_cart_items', 'prevent_checkout_display_notice' ); // for cart and checkout
function prevent_checkout_display_notice() {
// DEFINE HERE the return button URL and title
$button_url = '#';
$button_title = 'Go there';
// Display message if the non mandatory product categories are not in all cart items
if ( has_not_mandatory_category() )
wc_add_notice(
sprintf( __( 'Please choose at least one bento. <a class="button" href="%s">%s</a>', 'your_theme_domain'),
$button_url,
$button_title
), 'error'
);
}
Code goes in function.php file of your active child theme (or active theme).
Tested and works…
Instead of targeting non mandatory product categories you should do the contrary…
I have a checkout page where I want to:
Remove selected product attribute values from product variation title item.
Remove the quantity from item title too.
Display the different product attribute value such as size, color and the quantity on different rows, for this product variation item.
I want to display on my checkout page:
Aria Sport Shorts (the product title)
Color: Dusty Pink
Size: Small
QTY: 1
Instead of this:
Is it possible? Where I should start to make this real?
UPDATED
1) Since WooCommerce 3+, to remove attribute values from Product variation title and to display them in a separate row will need to use this 2 dedicated simple hooks (in checkout page).
Removing attribute values from Product variation title:
add_filter( 'woocommerce_product_variation_title_include_attributes', 'variation_title_not_include_attributes' );
function variation_title_not_include_attributes( $boolean ){
if ( ! is_cart() )
$boolean = false;
return $boolean;
}
Display Product variation attributes label and values in separate rows:
add_filter( 'woocommerce_is_attribute_in_product_name', 'remove_attribute_in_product_name' );
function remove_attribute_in_product_name( $boolean){
if ( ! is_cart() )
$boolean = false;
return $boolean;
}
2) Checkout page - Remove the quantity from the product title and add it back in a separate row.
Remove the quantity from the product title:
add_filter( 'woocommerce_checkout_cart_item_quantity', 'remove_product_variation_qty_from_title', 10, 3 );
function remove_product_variation_qty_from_title( $quantity_html, $cart_item, $cart_item_key ){
if ( $cart_item['data']->is_type('variation') && is_checkout() )
$quantity_html = '';
return $quantity_html;
}
Add back the cart item quantity in a separate row:
add_filter( 'woocommerce_get_item_data', 'filter_get_item_data', 10, 2 );
function filter_get_item_data( $item_data, $cart_item ) {
if ( $cart_item['data']->is_type('variation') && is_checkout() )
$item_data[] = array(
'key' => __('QTY'),
'display' => $cart_item['quantity']
);
return $item_data;
}
Code goes in function.php file of your active child theme (or theme) or also in any plugin file.
Tested in Woocommerce version 3+ and works. You should maybe need to make some styling changes…