In WooCommerce, I'm trying to create a one-page landing page with a product and checkout.
When the page is loaded I want the cart to empty.
But I want to be able to add to cart, and checkout on the same page.
I want to achieve this only on pages with a specific page-template.
I'm working from Clear Woocommerce Cart on Page Load Even for logged in users answer code.
This is what I have:
?>
<?php
//epmty cart
if (! WC()->cart->is_empty() ) {
WC()->cart->empty_cart( true );
}
<?php
// show add to cart button
echo do_shortcode( '[add_to_cart id='22']');
?>
// allow checkout Even though Cart Is Empty
add_filter( 'woocommerce_checkout_redirect_empty_cart', '__return_false' );
add_filter( 'woocommerce_checkout_update_order_review_expired', '__return_false' );
?>
<?php
echo do_shortcode( '[woocommerce_checkout' );
?>
The problem I think is that the page refreshes upon add to cart and therefor empties the cart again. How do I make it run only once? or is there a better way to clear the cart?
Update 2
You could try the following code in your template:
$current_product_id = 22; // The product ID
$cart = WC()->cart; // The WC_Cart Object
// When cart is not empty
if ( ! $cart->is_empty() ) {
// Loop through cart items
foreach( $cart->get_cart() as $cart_item_key => $cart_item ) {
// If the cart item is not the current defined product ID
if( $current_product_id != $cart_item['product_id'] ) {
$cart->remove_cart_item( $cart_item_key ); // remove it from cart
}
}
}
// When cart is empty
else {
// allow checkout Event
add_filter( 'woocommerce_checkout_redirect_empty_cart', '__return_false' );
add_filter( 'woocommerce_checkout_update_order_review_expired', '__return_false' );
}
// show add to cart button
echo do_shortcode( "[add_to_cart id='22']");
// show Checkout
echo do_shortcode( "[woocommerce_checkout]" );
Related
A specific WooCommerce product can only be by itself in the cart.
So how do I clear the cart while adding this specific product to the cart? and how can I remove this specific product from the cart when adding any other product?
I've figured out how to empty the cart when adding a specific product but I don't know how to remove this specific product from the cart when adding any other product.
The following will remove conditionally cart items based on a specific product:
When the specific product is added to cart, all other items are removed.
When any other product is added to cart, it removes the specific product (if it's in cart)
Here is the code:
// Remove conditionally cart items based on a specific product (item)
add_action( 'woocommerce_before_calculate_totals', 'remove_cart_items_conditionally', 10, 1 );
function remove_cart_items_conditionally( $cart ) {
// HERE define your specific product ID
$specific_product_id = 37;
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
$cart_items = $cart->get_cart(); // Cart items array
$items_count = count($cart_items); // Different cart items count
// Continue if cart has at least 2 different cart items
if ( $items_count < 2 )
return;
$last_item = end($cart_items); // Last cart item data array
$is_last_item = false; // Initializing
// Check if the specific product is the last added item
if ( in_array($specific_product_id, array( $last_item['product_id'], $last_item['variation_id'] ) ) ) {
$is_last_item = true;
}
// Loop through cart items
foreach ( $cart_items as $cart_item_key => $cart_item ) {
// Remove all others cart items when specific product ID is the last added to cart
if ( ! in_array($specific_product_id, array( $cart_item['product_id'], $cart_item['variation_id'] ) ) && $is_last_item ) {
$cart->remove_cart_item( $cart_item_key );
}
// Remove the specific item when its is not the last added to cart
elseif ( in_array($specific_product_id, array( $cart_item['product_id'], $cart_item['variation_id'] ) ) && ! $is_last_item ) {
$cart->remove_cart_item( $cart_item_key );
}
}
}
Code goes on function.php file of your active child theme (or active theme). Tested and works.
The above works perfectly! You can also add a notice to the cart once items have been removed to help with the user experience. Add this to the // Loop through cart items section in the first if statement:
wc_add_notice( __( 'Product XYZ has been removed from your cart because...', 'theme_domain' ), 'notice' );
I am using Woocommerce and I have integrated some custom fields to allow the user to specify new values that I will later append to the product title.
I am using update_post_meta/get_post_meta to save the new information. This part works fine.
Then I used the filter woocommerce_product_title to update the title. This filter is working fine when there is the use of $product->get_title() but will do nothing when using $product->get_name()which isn't an issue because in some places I don't want to append the new information.
I also used the filter the_title for the product page.
Basically my code looks like below where return_custom() will be the function than will build the new information based on the product ID.
function update_title($title, $id = null ) {
$prod=get_post($id);
if (empty($prod->ID) || strcmp($prod->post_type,'product')!=0 ) {
return $title;
}
return $title.return_custom($id);
}
function update_product_title($title, $product) {
$id = $product->get_id();
return $title.return_custom($id);
}
add_filter( 'woocommerce_product_title', 'update_product_title', 9999, 2);
add_filter( 'the_title', 'update_title', 10, 2 );
The issue arise when adding the product to the cart. The name used is the default one so my below code isn't enough to update the product name used in the cart. Same thing for the notification mails. I suppose this is logical since the email will use the information of the cart.
I am pretty sure that everything is happening inside add_to_cart() but I am not able to find any filter/hook related to the product name.
How to make sure the name used in the cart is good? What filter/hook shoud I consider in addition to the ones I am already using in order to append my new information to the product title within the cart?
I want to make sure that the new title will be seen during all the shopping process. From the product page until the notification mail.
The following will allow you to customize the product name in cart, checkout, orders and email notifications, just with one hooked function:
// Just used for testing
function return_custom( $id ) {
return ' - (' . $id . ')';
}
// Customizing cart item name in cart, checkout, orders and email notifications
add_action( 'woocommerce_before_calculate_totals', 'set_custom_cart_item_name', 10, 1 );
function set_custom_cart_item_name( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
// Required since Woocommerce version 3.2 for cart items properties changes
if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 )
return;
// Loop through cart items
foreach ( $cart->get_cart() as $cart_item ) {
// Get the product name and the product ID
$product_name = $cart_item['data']->get_name();
$product_id = $cart_item['data']->get_id();
// Set the new product name
$cart_item['data']->set_name( $product_name . return_custom($product_id) );
}
}
Code goes on function.php file of your active child theme (or active theme). Tested and works.
Try using woocommerce_cart_item_name filter.
[woocommerce_cart] shortcode uses cart/cart.php template, and the code displaying the title is this:
if ( ! $product_permalink ) {
echo wp_kses_post( apply_filters( 'woocommerce_cart_item_name', $_product->get_name(), $cart_item, $cart_item_key ) . ' ' );
} else {
echo wp_kses_post( apply_filters( 'woocommerce_cart_item_name', sprintf( '%s', esc_url( $product_permalink ), $_product->get_name() ), $cart_item, $cart_item_key ) );
}
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 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).
I have added code to only allow 1 item in the cart at one time. However, when a user adds any item to their cart, it does NOT direct them to the Cart page.
Here is the code that I am using:
add_filter( 'woocommerce_add_cart_item_data', 'woo_custom_add_to_cart' );
function woo_custom_add_to_cart( $cart_item_data ) {
global $woocommerce;
$woocommerce->cart->empty_cart();
wc_add_notice( 'WARNING MESSAGE - You can only have 1 Item in your Cart. Previous Items have been removed.', 'error' );
return $cart_item_data;
}
So my goal is to keep this function and error message but take the user to the cart. I am sure there is something in this code that is preventing the user from going to the cart and staying on the product page.
Thanks in advance for your help!
If you add an error then WooCommerce won't redirect. See source. You can trying setting the notice type to "notice".
Clear cart when item is added:
add_filter( 'woocommerce_add_to_cart_validation', 'so_41002991_empty_cart' );
function so_41002991_empty_cart( $valid ){
WC()->cart->empty_cart();
wc_add_notice( __( 'WARNING MESSAGE - You can only have 1 Item in your Cart. Previous Items have been removed.', 'your-plugin' ), 'notice' );
return $valid;
}
There's a WooCommerce setting that will enable redirect to cart. But there's also a filter that you can optionally use:
add_filter( 'woocommerce_add_to_cart_redirect', 'so_41002991_redirect_to_cart' );
function so_41002991_redirect_to_cart( $url ){
return wc_get_cart_url();
}