Woocommerce Cart - Conditional Items Categories Validation

WooCommerce Cart - Conditional Items categories validation

Updated (2019)

Like everything is turning around your exclusive category category X, you need to use a conditional for this category.

And you have chance because there is a special function that you can use in combination with woocommerce product categories. Lets say that **cat_x** is the slug for your exclusive category, as you know it yet product_cat is the argument to get products categories.

So with has_term () conditional function, you are going to use this:

if ( has_term ('cat_x', 'product_cat', $item_id ) ) { // or $product_id
// doing something when product item has 'cat_x'
} else {
// doing something when product item has NOT 'cat_x'
}

We need to run the cart items 2 times in a foreach loop:

  • To detect if there is a cat_x item in that car.
  • To remove other items if cat_x is detected for one item in the cart and to fire the right messages.

In the code below, I have changed to a more useful hook. This hook will check what you have in your cart. The idea is to removed other categories items in the cart when there is a 'cat_x' item added in cart.

The code is well commented. At the end you will find different notices that are fired. You will need to put your real text in each.

add_action( 'woocommerce_check_cart_items', 'checking_cart_items' );
function checking_cart_items() {
// Set your product category slug
$category = 'cat_x';

$number_of_items = sizeof( WC()->cart->get_cart() );
$found = false; // Initializing
$notice = ''; // Initializing

if ( $number_of_items > 0 ) {

// Loop through cart items
foreach ( WC()->cart->get_cart() as $cart_item ) {
$product = $cart_item['data'];
$product_id = $cart_item['product_id'];

// Detecting if the defined category is in cart
if ( has_term( $category, 'product_cat', $product_id ) ) {
$found = true;
break; // Stop the loop
}
}

// Re-loop through cart items
foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
$product = $cart_item['data'];
$product_id = $cart_item['product_id'];

if ( $found ) // If the defined product category is in cart
{
if ( $number_of_items == 1 ) { // only one defined product category
if ( empty( $notice ) ){
$notice = '1';
}
}
if ( $number_of_items >= 2 ) { // The defined product category + other categories in cart

// removing other categories items from cart
if ( ! has_term( $category, 'product_cat', $product_id ) ) {
WC()->cart->remove_cart_item( $cart_item_key ); // removing item from cart
if ( empty( $notice ) || $notice == '1' ){
$notice = '2';
}
}
}
} else { // Only other categories items

if ( empty( $notice ) ){
$notice = '3';
}
}
}

// Firing woocommerce notices
if ( $notice == '1' ) { // message for an 'cat_x' item only (alone)
wc_add_notice( sprintf( '<p class="woocommerce-error">bla bla bla one category X item in the cart</p>' ), 'success' );
} elseif ( $notice == '2' ) { // message for an 'cat_x' item and other ones => removed other ones
wc_add_notice( sprintf( '<p class="woocommerce-error">bla bla bla ther is already category X in the cart => Other category items has been removed</p>' ), 'error' );
} elseif ( $notice == '3' ) { // message for other categories items (if needed)
wc_add_notice( sprintf( '<p class="woocommerce-error">bla bla bla NOT category X in the cart</p>' ), 'success' );
}
}
}

Is not possible for me to really test this code (but it doesn't throws errors)…


@edit

We can use something else than notices… everything is possible. But it's a good starting solution, to fine tune.

You will need to replace 'cat_x' by your real category slug (in the beginning)

Conditional custom checkout fields based on product category in Woocommerce

First the conditional function code that you are using is really old, outdated and will not work when cart items are product variations (so for variable products). Here below is the compact and working conditional function…
It can work with any product category term ID, slug, name or an array of values:

function is_product_cat_in_cart( $categories ) {
foreach ( WC()->cart->get_cart() as $cart_item ) {
if (has_term ( $categories, 'product_cat', $cart_item['product_id'] ) )
return true;
}
return false;
}

Now the rest of your code has a lot of mistakes or little errors.

It also use outdated or deprecated hooks like:

  • woocommerce_checkout_update_order_meta replaced by a much appropriated hook.
  • woocommerce_email_order_meta_keys is deprecated since a lot of time.

You can also merge some code together in the same hooked functions.

You don't need the conditional function everywhere. It's just needed for checkout fields conditional display.

Here is your revisited code (for woocommerce version 3 and above):

// Add fields for Emergency Contact & Medical Information to the checkout page
add_action('woocommerce_after_order_notes', 'customise_checkout_field', 20, 1 );
function customise_checkout_field( $checkout ){
$domain = 'woocommerce';

// There is a class in the cart so show additional fields
if ( is_product_cat_in_cart( 'class' ) ):

echo '<div id="customise_checkout_field">
<h3>' . __( 'Emergency Contact & Medical Information', $domain ) . '</h3>';

woocommerce_form_field( 'emergency_contact', array(
'type' => 'text',
'class' => array( 'emergency-contact form-row-wide' ),
'label' => __( 'Emergency Contact', $domain ) ,
'placeholder' => __( 'Please enter first & last name', $domain ),
'required' => true,
), $checkout->get_value('emergency_contact') );

woocommerce_form_field( 'emergency_contact_relationship', array(
'type' => 'text',
'class' => array( 'emergency-contact-relationship form-row-wide' ),
'label' => __( 'What is your relationship with this person?', $domain ),
'placeholder' => __( 'Example: Mother', $domain ) ,
'required' => true,
), $checkout->get_value('emergency_contact_relationship') );

woocommerce_form_field( 'emergency_contact_phone', array(
'type' => 'text',
'class' => array( 'emergency-contact-phone form-row-wide' ),
'label' => __( 'What is their phone number?', $domain ),
'placeholder' => __( '(555) 555-5555', $domain ),
'required' => true,
), $checkout->get_value('emergency_contact_phone') );

woocommerce_form_field( 'medical_medicine', array(
'type' => 'textarea',
'class' => array( 'medical-medicine form-row-wide' ) ,
'label' => __( 'Do you have any medical conditions and are you taking any medications we need to be aware of?', $domain ),
'placeholder' => __( 'If not please write in "none"', $domain ),
'required' => true,
) , $checkout->get_value('medical_medicine') );
echo '</div>';

endif;
}

// Add custom checkboxes to woocommerce checkout page for Photo Release, Mailing List & Release of Liability
add_action( 'woocommerce_review_order_before_submit', 'custom_checkout_fields' );
function custom_checkout_fields() {
$checkout = WC()->checkout;
$domain = 'woocommerce';

echo '<div id="custom_checkout_fields">
<h3>'.__( 'Mailing Lists', $domain ).'</h3>
<p>'.__( 'Mailing List boilerplate', $domain ).'</p>';

woocommerce_form_field( 'mailing_consent', array(
'type' => 'checkbox',
'class' => array('input-checkbox'),
'label' => __( 'Please add me to Nonprofit\'s electronic and paper mailing lists.', $domain ),
'required' => false,
'clear' => true,
'default' => 1 //This will pre-select the checkbox
), $checkout->get_value( 'mailing_consent' ) );

// There is a class in the cart so show additional fields
if ( is_product_cat_in_cart( 'class' ) ):

echo '<h3>'.__( 'Photo Release', $domain ).'</h3>
<p>'.__( 'Photo Release Boilerplate', $domain ).'</p>';

woocommerce_form_field( 'photo_consent', array(
'type' => 'checkbox',
'class' => array('input-checkbox'),
'label' => __( 'I agree to the Photo Release as outlined above.', $domain ),
'required' => false,
'clear' => true,
'default' => 1 //This will pre-select the checkbox
), $checkout->get_value( 'photo_consent' ) );

echo '<h3>'.__( 'Release of Liability', $domain ).'</h3>
<p>'.__( 'Release of Liability Boilerplate', $domain ).'</p>';

woocommerce_form_field( 'liability_release', array(
'type' => 'checkbox',
'class' => array('input-checkbox'),
'label' => __( 'I agree to the Photo Release as outlined above.', $domain ),
'required' => true,
'clear' => true,
'default' => 1 //This will pre-select the checkbox
), $checkout->get_value( 'liability_release' ) );

endif;

echo '</div>';
}

// Custom checkout fields validation
add_action('woocommerce_checkout_process', 'custom_checkout_fields_process');
function custom_checkout_fields_process() {
$domain = 'woocommerce';

if ( isset($_POST['emergency_contact']) && empty($_POST['emergency_contact']) )
wc_add_notice( __( 'Please list an emergency contact.', $domain ) , 'error' );

if ( isset($_POST['emergency_contact_relationship']) && empty($_POST['emergency_contact']) )
wc_add_notice( __( 'Please indicate your relationship with your emergency contact.', $domain ), 'error' );

if ( isset($_POST['emergency_contact_phone']) && empty($_POST['emergency_contact']) )
wc_add_notice( __( 'Please list a phone number for your emergency contact.', $domain ), 'error' );

if ( isset($_POST['medical_medicine']) && empty($_POST['emergency_contact']) )
wc_add_notice( __( 'Please list any medications or write in "none".', $domain ), 'error' );

// Other checkout fields
if ( ! isset( $_POST['liability_release'] ) && ! $_POST['liability_release'] && isset($_POST['photo_consent']) )
wc_add_notice( __( 'You must agree to the Release of Liability to register for this class. Please contact us with any questions.', $domain ), 'error' );
}

// Save custom checkout fields in the order meta data
add_action( 'woocommerce_checkout_create_order', 'custom_checkout_fields_in_order_meta_data', 20, 2 );
function custom_checkout_fields_in_order_meta_data( $order, $data ) {

if ( isset($_POST['emergency_contact']) && ! empty($_POST['emergency_contact']) )
$order->update_meta_data( 'emergency_contact', sanitize_text_field($_POST['emergency_contact']) );

if ( isset($_POST['emergency_contact_relationship']) && ! empty($_POST['emergency_contact_relationship']) )
$order->update_meta_data( 'emergency_contact_relationship', sanitize_text_field($_POST['emergency_contact_relationship']) );

if ( isset($_POST['emergency_contact_phone']) && ! empty($_POST['emergency_contact_phone']) )
$order->update_meta_data( 'emergency_contact_phone', sanitize_text_field($_POST['emergency_contact_phone']) );

if ( isset($_POST['medical_medicine']) && ! empty($_POST['medical_medicine']) )
$order->update_meta_data( 'medical_medicine', sanitize_text_field($_POST['medical_medicine']) );

if ( isset($_POST['mailing_consent']) && ! empty($_POST['mailing_consent']) )
$order->update_meta_data( 'mailing_consent', '1' );

if ( isset( $_POST['photo_consent']) && ! empty($_POST['photo_consent']) )
$order->update_meta_data( 'photo_consent', '1' );

if ( isset( $_POST['liability_release']) && ! empty($_POST['liability_release']) )
$order->update_meta_data( 'liability_release', '1' );
}

// Add the emergency contact fields to email notifications
add_filter( 'woocommerce_email_order_meta_fields', 'custom_checkout_field_email_order_meta', 20, 3 );
function custom_checkout_field_email_order_meta( $fields, $sent_to_admin, $order ) {
$domain = 'woocommerce';

if( ! $order->get_meta( 'emergency_contact' ) )
return $fields; // Exit if not set in the order

echo '<h2>'.__( 'Emergency Contact & Medical Information', $domain ).'</h2>';

$fields[] = array( 'label' => __( 'Emergency contact', $domain ),
'value' => $order->get_meta( 'emergency_contact' ) );

$fields[] = array( 'label' => __( 'Emergency Contact Relationship', $domain ),
'value' => $order->get_meta( 'emergency_contact_relationship' ) );

$fields[] = array( 'label' => __( 'Emergency Contact Phone', $domain ),
'value' => $order->get_meta( 'emergency_contact_phone' ) );

$fields[] = array( 'label' => __( 'Medical Conditions & Medications', $domain ),
'value' => $order->get_meta( 'medical_medicine' ) );

return $fields;
}

// Display some custom checkout fields in Order edit pages
add_action( 'woocommerce_admin_order_data_after_billing_address', 'display_custom_field_on_order_edit_pages', 20, 1 );
function display_custom_field_on_order_edit_pages( $order ){
$domain = 'woocommerce';

$billing_name = $order->get_billing_first_name().' '.$order->get_billing_last_name();

if( $order->get_meta('mailing_consent') )
echo '<p>' . $billing_name . __( ' agreed to the be added to Nonprofit\'s mailing lists.', $domain ).'</p>';

if( $order->get_meta('photo_consent') )
echo '<p>' . $billing_name . __( ' agreed to the Photo Release.', $domain ).'</p>';

if( $order->get_meta('liability_release') )
echo '<p>' . $billing_name . __( ' agreed to the Release of Liability.', $domain ).'</p>';
}

Code goes in function.php file of your active child theme (or active theme). Tested and works.

Conclusion: If some code doesn't work in function.php file, it will not work better in a plugin. But if you want you can add it in a plugin, if you prefer.

Add to cart validation based on category and custom textfield in WooCommerce

  • The woocommerce_add_to_cart_validation hook contains not one but 5 parameters
  • Use wc_add_notice opposite <script>alert('hola');</script>

So you get

function filter_woocommerce_add_to_cart_validation( $passed, $product_id, $quantity, $variation_id = null, $variations = null ) {
// Set (multiple) categories
$categories = array ( 'flower-arrangements', 'categorie-1' );

// If passed & has_term
if ( $passed && has_term( $categories, 'product_cat', $product_id ) ) {
// Field is empty
if( empty( $_POST['text_flowers_label'] ) ) {
wc_add_notice( __( 'Please enter a value into the text flowers label', 'woocommerce' ), 'error' );
$passed = false;
}
}

return $passed;
}
add_filter( 'woocommerce_add_to_cart_validation', 'filter_woocommerce_add_to_cart_validation', 10, 5 );

Woocommerce add fee if cart contains items from both categories

You can iterate the loop of $cart->get_cart() get category using get_the_terms() and push to an array then you can loop $must_categories to check both category are available or not.

add_action( 'woocommerce_cart_calculate_fees','custom_pcat_fee', 20, 1 );
function custom_pcat_fee( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;

// Categories in a coma separated array
$must_categories = array('drinks','bundles');
$fee_amount = 0;

$product_cat = array();

// Loop through cart items
foreach( $cart->get_cart() as $cart_item ){
$terms = get_the_terms( $cart_item['product_id'], 'product_cat' );
foreach ($terms as $term) {
$product_cat[] = $term->slug;
}
}

array_unique( $product_cat );

foreach ( $must_categories as $key => $must_cat ) {

if( in_array($must_cat, $product_cat) ){
$fee_amount = -1;
}else{
$fee_amount = 0;
break;
}

}

// Adding the fee
if ( $fee_amount < 0 ){
// Last argument is related to enable tax (true or false)
WC()->cart->add_fee( __( "Kombucha Bundle Discount", "woocommerce" ), $fee_amount, false );
}
}

Tested and works

Restricting cart items to be from the same product category in WooCommerce

Making a custom function hooked in woocommerce_add_to_cart_validation filter hook is going to do the job in a much more simpler way, without any need to set an array of categories.

So your code will be much more faster and compact. Additionally you can display a custom notice to warn the customer.

This code will avoid adding to cart, if an item of a different category is in cart:

add_filter( 'woocommerce_add_to_cart_validation', 'add_to_cart_validation_callback', 10, 3 );
function add_to_cart_validation_callback( $passed, $product_id, $quantity) {
// HERE set your alert text message
$message = __( 'MY ALERT MESSAGE.', 'woocommerce' );

if( ! WC()->cart->is_empty() ) {
// Get the product category terms for the current product
$terms_slugs = wp_get_post_terms( $product_id, 'product_cat', array('fields' => 'slugs'));

// Loop through cart items
foreach (WC()->cart->get_cart() as $cart_item ){
if( ! has_term( $terms_slugs, 'product_cat', $cart_item['product_id'] )) {
$passed = false;
wc_add_notice( $message, 'error' );
break;
}
}
}
return $passed;
}

Code goes in function.php file of your active child theme (or theme) or also in any plugin file.

This code is tested and it works



Restricting cart items to be only from different product categories:

Replace in the function the condition:

if( ! has_term( $product_cats, 'product_cat', $cart_item['product_id'] )) { 

by

if( has_term( $product_cats, 'product_cat', $cart_item['product_id'] )) {

Avoid add to cart for specific product categories if user is unlogged in Woocommerce

The conditional tag is_product_category() only target product category archive pages. Instead you can use WordPress conditional function has_term().

There is 2 ways to avoid specific products being added to cart for no logged user…

1) Using Add to cart validation hook:

// Avoid add to cart conditionally
add_filter( 'woocommerce_add_to_cart_validation', 'avoid_add_to_cart_conditionally', 20, 3 );
function avoid_add_to_cart_conditionally( $passed, $product_id, $quantity) {
// HERE your product categories (can be IDs, slugs or names terms)
$terms = array( 'gekoelde-bier', 'bierkoerier');

if( has_terms( $terms, 'product_cat', $product_id ) && ! is_user_logged_in() ){
// Displaying a custom notice (optional)
wc_add_notice( __('Only logged in users are allowed to purchase this item. Please register.'), 'error' );

$passed = false;
}

return $passed;
}

Code goes in functions.php file of your active child theme (or active theme). Tested and works.

Sample Image


2) Using is_purchasable product property (It will remove add to cart button):

add_filter('woocommerce_is_purchasable','conditional_purchasable_products', 20, 2);
function conditional_purchasable_products( $is_purchasable, $product ) {
// HERE your product categories (can be IDs, slugs or names terms)
$terms = array( 'gekoelde-bier', 'bierkoerier');

$product_id = $product->get_id(); // The product ID

if( has_terms( $terms, 'product_cat', $product_id ) && ! is_user_logged_in() ){
$is_purchasable = false;
}

return $is_purchasable;
}

Code goes in functions.php file of your active child theme (or active theme). Tested and works.



Targeting the parent product category terms too.

You will use the following custom conditional function to replace has_term() Wordpress function:

// Custom conditional function that handle parent product categories too
function has_product_categories( $categories, $product_id = 0 ) {
$parent_term_ids = $categories_ids = array(); // Initializing
$taxonomy = 'product_cat';
$product_id = $product_id == 0 ? get_the_id() : $product_id;

if( is_string( $categories ) ) {
$categories = (array) $categories; // Convert string to array
}

// Convert categories term names and slugs to categories term ids
foreach ( $categories as $category ){
$result = (array) term_exists( $category, $taxonomy );
if ( ! empty( $result ) ) {
$categories_ids[] = reset($result);
}
}

// Loop through the current product category terms to get only parent main category term
foreach( get_the_terms( $product_id, $taxonomy ) as $term ){
if( $term->parent > 0 ){
$parent_term_ids[] = $term->parent; // Set the parent product category
$parent_term_ids[] = $term->term_id; // (and the child)
} else {
$parent_term_ids[] = $term->term_id; // It is the Main category term and we set it.
}
}
return array_intersect( $categories_ids, array_unique($parent_term_ids) ) ? true : false;
}

Then for both hooked functions, you will replace the following line :

if( has_terms( $terms, 'product_cat', $product_id ) && ! is_user_logged_in() ){

By this line:

if( has_product_categories( $terms, $product_id ) && ! is_user_logged_in() ){

Code goes in functions.php file of your active child theme (or active theme). Tested and works.



Related Topics



Leave a reply



Submit