I've been tasked with creating the most awkward buying process my client could possibly come up with.
When buying a product, the customer proceeds to a multi-step process where they build up a bespoke 'package' (To make things more complicated some parts of the package are priced on choices made earlier in the process). Now, because they want the customer to be able to adjust the quantity of the original product, the only solution I can come up with in the basket is to show the original product as normal, then have a second 'Package' product which lists the other parts they chose and the total price for those choices as the line item cost. This isn't too much of a problem, and hopefully stopping them changing the package quantity won't be too complicated either.
Now, when they delete the product, the package should go as well. I haven't tackled this yet but I suspect I can add some meta data to the cart items to link them, then automatically delete the package product in the item_removed
handler. (Basically I'll give the item a unique package_id
in meta data, then remove anything else in the basket that references that id)
My issue is stopping them delete the package. Ideally I just want to show an error that it is linked to the main product, and they need to delete that instead if they don't want it.
So far I've tried the following two options, but neither work correctly -
- Using
woocommerce_remove_cart_item
and throwing an exception. This stops the item being removed, but the exception message isn't shown as a notice, and adding a call towc_add_notice
does nothing (I expect because I'm throwing an exception, but anything other than an exception here is just going to return to thecart->remove_cart_item()
function and the item will be removed as normal.) - Using
woocommerce_cart_item_removed
and putting the item back. This seems to work, although it's a bit messy to remove the item just to put it back. The issue with this is that it still shows the 'item removed' success message even though my error notice appears and the item is back as it was.
Is there not an easy way to just stop an item being removed? I'm sure other Woocommerce features / plugins provide similar functionality for dependencies, but I can only see the before/after hooks in the code. It seems that the actual unset
call runs regardless.
I've been tasked with creating the most awkward buying process my client could possibly come up with.
When buying a product, the customer proceeds to a multi-step process where they build up a bespoke 'package' (To make things more complicated some parts of the package are priced on choices made earlier in the process). Now, because they want the customer to be able to adjust the quantity of the original product, the only solution I can come up with in the basket is to show the original product as normal, then have a second 'Package' product which lists the other parts they chose and the total price for those choices as the line item cost. This isn't too much of a problem, and hopefully stopping them changing the package quantity won't be too complicated either.
Now, when they delete the product, the package should go as well. I haven't tackled this yet but I suspect I can add some meta data to the cart items to link them, then automatically delete the package product in the item_removed
handler. (Basically I'll give the item a unique package_id
in meta data, then remove anything else in the basket that references that id)
My issue is stopping them delete the package. Ideally I just want to show an error that it is linked to the main product, and they need to delete that instead if they don't want it.
So far I've tried the following two options, but neither work correctly -
- Using
woocommerce_remove_cart_item
and throwing an exception. This stops the item being removed, but the exception message isn't shown as a notice, and adding a call towc_add_notice
does nothing (I expect because I'm throwing an exception, but anything other than an exception here is just going to return to thecart->remove_cart_item()
function and the item will be removed as normal.) - Using
woocommerce_cart_item_removed
and putting the item back. This seems to work, although it's a bit messy to remove the item just to put it back. The issue with this is that it still shows the 'item removed' success message even though my error notice appears and the item is back as it was.
Is there not an easy way to just stop an item being removed? I'm sure other Woocommerce features / plugins provide similar functionality for dependencies, but I can only see the before/after hooks in the code. It seems that the actual unset
call runs regardless.
1 Answer
Reset to default 0Maybe there is a way of doing this, but briefly scanning through the relevant functions suggests that I basically have no control over the item being removed, or the call to wc_add_notice
to mark is as removed.
As such I ended up with the following fairly simple, but slightly ugly method:
add_action('woocommerce_cart_item_removed', function($key, $cart){
// SNIP - My logic to decide if this item can't be removed by itself
$cart->restore_cart_item($key);
wc_add_notice('This package is tied to another product in your basket.', 'error');
add_filter('woocommerce_add_success', '__return_false');
}, 5, 2);
Basically I restore the item and add an error notice to inform the user that we couldn't remove it. Then for the remainder of the current request I filter any new success notices to replace the text with FALSE
.
The code to display notices uses array_filter
to exclude any elements in the list that equate to false, so while a message does get added, it has no text and is not displayed.
Once a basket item is removed, the code path from WC_Form_Handler
simply adds the "removed" notice and performs a wp_safe_redirect
, so overriding notices at this point should not have any other adverse effects as far as I can see.