In Woocommerce, I want to prevent users from adding to cart if a specific attribute is selected.
The attribute name is "Production Time" and the specific value we want to disable from purchasing is "Urgent".
The code below is from this link - Hide Add to Cart button in Woocommerce product variations for a specific attribute value
However, it only works with numbers. When the attribute is a text, it doesn't work.
add_filter( 'woocommerce_variation_is_purchasable', 'conditional_variation_is_purchasable', 20, 2 );
function conditional_variation_is_purchasable( $purchasable, $product ) {
## ---- Your settings ---- ##
$taxonomy = 'production-time';
$term_name = 'urgent';
## ---- The active code ---- ##
$found = false;
// Loop through all product attributes in the variation
foreach ( $product->get_variation_attributes() as $variation_attribute => $term_slug ){
$attribute_taxonomy = str_replace('attribute_', '', $variation_attribute); // The taxonomy
$term = get_term_by( 'slug', $term_slug, $taxonomy ); // The WP_Term object
// Searching for attribute 'pa_size' with value 'XL'
if($attribute_taxonomy == $taxonomy && $term->name == $term_name ){
$found = true;
break;
}
}
if( $found )
$purchasable = false;
return $purchasable;
}
Related
Please pardon me if there's a better way to do this as I am not very familiar with this code. I would like to display only the link to the homepage and the current product on the breadcrumb.
Desire result:
Currently:
I found the code for the breadcrumb, is there a way to only display the first and last crumb regardless of the hierarchy?
foreach ( $breadcrumb as $key => $crumb ) {
echo $before;
if ( ! empty( $crumb[1] ) && sizeof( $breadcrumb ) !== $key ) {
echo '' . esc_html( $crumb[0] ) . '';
} else if(!is_product() && !flatsome_option('wc_category_page_title')) {
echo esc_html( $crumb[0] );
}
echo $after;
// Single product or Active title
if(is_product() || flatsome_option('wc_category_page_title')){
$key = $key+1;
if ( sizeof( $breadcrumb ) > $key) {
echo ' <span class="divider">'.$delimiter.'</span> ';
}
} else{
// Category pages
if ( sizeof( $breadcrumb ) !== $key + 1 ) {
echo ' <span class="divider">'.$delimiter.'</span> ';
}
}
}
The reason why I am doing this is that some of the products have multiple categories and by default, it will only show the breadcrumb for the primary category. I would rather make a truncated version as suggested by the owner.
I was also wondering if I can simply dynamically retrieve the product title and link + static homepage link, make it into a shortcode so that I can paste it in the product page.
Hi – the first example in the answer above, also removes the shop from woocommerce breadcrumb. Here is a working example, that only removes the category:
// remove only the category from woocommerce breadcrumbs
add_filter( 'woocommerce_get_breadcrumb', 'custom_breadcrumb', 20, 2 );
function custom_breadcrumb( $crumbs, $breadcrumb ) {
//print the array and look for the key with the category
//echo '<pre>';
//print_r($crumbs);
//echo '</pre>';
//unset($crumbs[2]); in my case it is key 2
// only on the single product page
if ( ! is_product() ) {
return $crumbs;
} else {
unset($crumbs[2]); // this isn't enough, it would leave a trailing delimiter
$newBreadC = array_values($crumbs); //therefore create new array
return $newBreadC; //return the new array
}
}
If you want to remove categories and subcategories from product breadcrumbs on the product page you can use the woocommerce_get_breadcrumb hook.
// change the breadcrumb on the product page
add_filter( 'woocommerce_get_breadcrumb', 'custom_breadcrumb', 20, 2 );
function custom_breadcrumb( $crumbs, $breadcrumb ) {
// only on the single product page
if ( ! is_product() ) {
return $crumbs;
}
// gets the first element of the array "$crumbs"
$new_crumbs[] = reset( $crumbs );
// gets the last element of the array "$crumbs"
$new_crumbs[] = end( $crumbs );
return $new_crumbs;
}
The code has been tested and works. Add it to your active theme's functions.php.
A good alternative is to set the primary product category for each product. You can do this by installing the Yoast SEO plugin.
You can use the _yoast_wpseo_primary_product_cat product meta to set the id of the primary product category.
After setting the primary category id by editing the product in the
backend or importing a .csv file you will only need to change the
permalink and breadcrumbs based on the primary product category.
To update the product permalink:
// update the product permalink based on the primary product category
add_filter( 'wc_product_post_type_link_product_cat', 'change_product_permalink_by_cat', 10, 3 );
function change_product_permalink_by_cat( $term, $terms, $post ) {
// get the primary term as saved by Yoast
$primary_cat_id = get_post_meta( $post->ID, '_yoast_wpseo_primary_product_cat', true );
// if there is a primary and it's not currently chosen as primary
if ( $primary_cat_id && $term->term_id != $primary_cat_id ) {
// find the primary term in the term list
foreach ( $terms as $term_key => $term_object ) {
if ( $term_object->term_id == $primary_cat_id ) {
// return this as the primary term
$term = $terms[ $term_key ];
break;
}
}
}
return $term;
}
To update the product breadcrumbs on the product page:
// change the breadcrumb on the product page
add_filter( 'woocommerce_get_breadcrumb', 'custom_breadcrumb', 20, 2 );
function custom_breadcrumb( $crumbs, $breadcrumb ) {
// only on the single product page
if ( ! is_product() ) {
return $crumbs;
}
global $product;
$new_crumbs = array();
if ( $product->get_meta( '_yoast_wpseo_primary_product_cat', true ) ) {
// gets the first element of the array "$crumbs"
$new_crumbs[] = reset( $crumbs );
// gets the id of the primary product category
$primary_cat_id = $product->get_meta( '_yoast_wpseo_primary_product_cat', true );
// create an array with all parent categories (based on the id of the primary product category)
$parent_categories = get_ancestors( $primary_cat_id, 'product_cat' );
$parent_categories = array_reverse($parent_categories);
$parent_categories[] = $primary_cat_id;
// for each product category it gets the name and the permalink
foreach ( $parent_categories as $cat_id ) {
$term = get_term_by( 'id', $cat_id, 'product_cat' );
$new_crumbs[] = array(
0 => $term->name,
1 => esc_url( get_term_link( $term, 'product_cat' ) )
);
}
// gets the last element of the array "$crumbs"
$new_crumbs[] = end( $crumbs );
} else {
// gets the first element of the array "$crumbs"
$new_crumbs[] = reset( $crumbs );
// gets the last element of the array "$crumbs"
$new_crumbs[] = end( $crumbs );
}
return $new_crumbs;
}
The code has been tested and works. Add it to your active theme's functions.php.
I want to prevent:
When a certain product is in the cart, no other product should be added
When there are other products in cart, the certain product should not be added
I have a code snippet that restricts the addition of more than one product. It works, but it shows the restriction message every time a 2nd product is added, whichever product this may be. This way there can never be multiple products in the cart.
add_filter( 'woocommerce_add_to_cart_validation', 'tidaweb_only_one_item_allowed_add_to_cart', 10, 3 );
function tidaweb_only_one_item_allowed_add_to_cart( $passed, $product_id, $quantity ) {
$cart_contents = WC()->cart->get_cart();
$cart_product_ids = [];
if(!empty($cart_contents))
{
foreach($cart_contents as $cart_item)
$cart_product_ids[] = $cart_item['product_id'];
}
$cart_product_ids = array_unique( $cart_product_ids );
if( count( $cart_product_ids ) >= 1 ) {
if(in_array($product_id, $cart_product_ids)) { // same product id passed
$passed = true;
} else {
// Set to false
$passed = false;
// Display a message
wc_add_notice( __( "You can only add one product to your cart at a time.", "woocommerce" ), "error" );
}
}
return $passed;
}
Any advice on what addition would be needed to my existing code to meet the above conditions?
To prevent when a certain product is in the cart, no other product should be added to it, and when there are other products in cart, this product should not be added, you can use:
woocommerce_add_to_cart_validation filter hook
WC_Cart::find_product_in_cart() function - which allows to check if product is in the cart and return cart item key.
So you get:
function filter_woocommerce_add_to_cart_validation( $passed, $product_id, $quantity, $variation_id = null, $variations = null ) {
// Add the specific product ID here
$specific_product_id = 30;
// Real product ID
$product_id = $variation_id > 0 ? $variation_id : $product_id;
// WC Cart
if ( WC()->cart ) {
// Get cart
$cart = WC()->cart;
// If cart is NOT empty
if ( ! $cart->is_empty() ) {
// Cart id
$product_cart_id = $cart->generate_cart_id( $specific_product_id );
// Find product in cart
$in_cart = $cart->find_product_in_cart( $product_cart_id );
// In cart & the current product ID does not match
if ( $in_cart && ( $specific_product_id != $product_id ) ) {
// Display an error message
wc_add_notice( __( 'You cannot add another product, there is already a specific product in cart', 'woocommerce' ), 'error' );
// False
$passed = false;
// NOT in cart & the current product ID matches
} elseif ( ! $in_cart && ( $specific_product_id == $product_id ) ) {
// Display an error message
wc_add_notice( __( 'You cannot add this product, there are already other products in cart', 'woocommerce' ), 'error' );
// False
$passed = false;
}
}
}
return $passed;
}
add_filter( 'woocommerce_add_to_cart_validation', 'filter_woocommerce_add_to_cart_validation', 10, 5 );
I have this code that works great for 2 products if one is added the other is removed.
But I would like to add 4 products to the category.
Only one can be in the cart, if one is in the cart and the customer changes mind and wants
a different one (from these 4 products) the one in the cart will be removed and the new choice added.
Notices are not important.
Any help very much appreciated.
add_action( 'woocommerce_add_to_cart', 'check_product_added_to_cart', 10, 6 );
function check_product_added_to_cart($cart_item_key, $product_id, $quantity, $variation_id, $variation, $cart_item_data) {
// Set HERE your targeted product ID
$target_product_ids1 = array(391);
$target_product_ids2 = array(393);
// Set HERE the product ID to remove
$item_id_to_remove1 = 393;
$item_id_to_remove2 = 391;
// Initialising some variables
$has_item1 = $has_item2 = $is_product_id1 = $is_product_id2 = false;
foreach( WC()->cart->get_cart() as $key => $item ){
// Check if the item to be removed 1 is in cart
if( $item['product_id'] == $item_id_to_remove1 ){
$has_item1 = true;
$key_to_remove1 = $key;
}
// Check if the item to be removed 2 is in cart
if( $item['product_id'] == $item_id_to_remove2 ){
$has_item2 = true;
$key_to_remove2 = $key;
}
// Check if we added to cart any targeted product IDs 1
if( in_array( $product_id, $target_product_ids1 ) ){
$is_product_id1 = true;
}
// Check if we added to cart any targeted product IDs 2
if( in_array( $product_id, $target_product_ids2 ) ){
$is_product_id2 = true;
}
}
if( $has_item1 && $is_product_id1 ){
WC()->cart->remove_cart_item($key_to_remove1);
// Optionaly displaying a notice for the removed item:
wc_add_notice( __( 'The product 1 "blab bla" has been removed from cart.', 'theme_domain' ), 'notice' );
}
if( $has_item2 && $is_product_id2 ){
WC()->cart->remove_cart_item($key_to_remove2);
// Optionaly displaying a notice for the removed item:
wc_add_notice( __( 'The product 2 "blab bla" has been removed from cart.', 'theme_domain' ), 'notice' );
}
}
I have an unique project using woocommerce. I created a form with Formidable Forms to add variable products from the front-end. It's working well with the code below. I have several attributes that are created. If the attributes are Not blank, they show up correctly as a variation drop-down option.
My problem is when the attribute - for example "Color" is blank and not submitted. The variation "Color" attribute is still created and the value is blank and the drop-down is empty. I've been searching for days to solve this issue. How to not create a variation/attribute if it's not submitted.
I also tried to programically change the set_variation to '' if a custom field value is empty. But that didn't work.
The code was found here:
Auto add all product attributes when adding a new product in Woocommerce
add_action( 'save_post', 'create_product_attributes_variations', 80, 3 );
function create_product_attributes_variations( $post_id, $post, $update ) {
## --- Checking --- ##
if ( $post->post_type != 'product') return; // Only products
// Exit if it's an autosave
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE )
return $post_id;
// Exit if it's an update
if( $update )
return $post_id;
// Exit if user is not allowed
if ( ! current_user_can( 'edit_product', $post_id ) )
return $post_id;
## -- Set product as variable and set quote option --- ##
update_post_meta( $post_id, 'qwc_enable_quotes', 'on');
wp_set_object_terms( $post_id, 'variable', 'product_type', false );
## --- The Settings for your product attributes --- ##
$visible = ''; // can be: '' or '1'
$variation = '1'; // can be: '' or '1'
## --- The code --- ##
// Get all existing product attributes
global $wpdb;
$attributes = $wpdb->get_results( "SELECT * FROM {$wpdb->prefix}woocommerce_attribute_taxonomies" );
$position = 0; // Auto incremented position value starting at '0'
$data = array(); // initialising (empty array)
// Loop through each exiting product attribute
foreach( $attributes as $attribute ){
// Get the correct taxonomy for product attributes
$taxonomy = 'pa_'.$attribute->attribute_name;
$attribute_id = $attribute->attribute_id;
$term_ids = get_terms(array('taxonomy' => $taxonomy, 'fields' => 'ids'));
$product_attribute = new WC_Product_Attribute();
// Set the related data in the WC_Product_Attribute object
$product_attribute->set_id( $attribute_id );
$product_attribute->set_name( $taxonomy );
$product_attribute->set_options( $term_ids );
$product_attribute->set_position( $position );
$product_attribute->set_visible( $visible );
$product_attribute->set_variation( $variation );
// Add the product WC_Product_Attribute object in the data array
$data[$taxonomy] = $product_attribute;
$position++; // Incrementing position
}
// Get an instance of the WC_Product object
$product = wc_get_product( $post_id );
// Set the array of WC_Product_Attribute objects in the product
$product->set_attributes( $data );
//Save main product to get its id
$id = $product->save();
///////
$variation = new WC_Product_Variation();
$variation->set_regular_price(5);
$variation->set_parent_id($id);
//Set attributes requires a key/value containing
// tax and term slug
$variation->set_attributes(array(
));
//Save variation, returns variation id
$variation->save();
}
This question already has answers here:
How to add variation stock status to Woocommerce product variation dropdown
(4 answers)
Closed 4 years ago.
I Would like next to variation to show "Out of Stock" when product variation is out of stock. Below, I am using the code from this answer:
add_filter( 'woocommerce_variation_option_name',
'customizing_variations_terms_name', 10, 1 );
function customizing_variations_terms_name( $term_name ){
if(is_admin())
return $term_name;
global $product;
$second_loop_stoped = false;
// Get available product variations
$product_variations = $product->get_available_variations();
// Iterating through each available product variation
foreach($product_variations as $variation){
$variation_id = $variation['variation_id'];
$variation_obj = new WC_Product_Variation( $variation_id );
## WOOCOMMERCE RETRO COMPATIBILITY ##
if ( version_compare( WC_VERSION, '3.0', '<' ) ) # BEFORE Version 3 (older)
{
$stock_status = $variation_obj->stock_status;
$stock_qty = intval($variation_obj->stock);
// The attributes WC slug key and slug value for this variation
$attributes_arr = $variation_obj->get_variation_attributes();
}
else # For newest verions: 3.0+ (and Up)
{
$stock_status = $variation_obj->get_stock_status();
$stock_qty = $variation_obj->get_stock_quantity();
// The attributes taxonomy key and slug value for this variation
$attributes_arr = $variation_obj->get_attributes();
}
if(count($attributes_arr) != 1) // Works only for 1 attribute set in the product
return $term_name;
// Get the terms for this attribute
foreach( $attributes_arr as $attr_key => $term_slug){
// Get the attribute taxonomy
$term_key = str_replace('attribute_', '', $attr_key );
// get the corresponding term object
$term_obj = get_term_by( 'slug', $term_slug, $term_key );
if( $term_obj->name == $term_name ){ // If the term name matches we stop the loops
$second_loop_stoped = true;
break;
}
}
if($second_loop_stoped)
break;
}
if( $stock_qty>0 )
return $term_name .= ' - ' . $stock_status . ' ('.$stock_qty.')';
else
return $term_name .= ' - ' . $stock_status;
}
but when test on my site, it show "Out of Stock" for all variations i have on selected product. For example on this product it show "Out of Stock" for all variations i have, but in fact, only variation "50 ליטר" is in real "Out Of Stock". other variations are In Stock ! I using Woocommerce 3.3.5 version. Can someone to tell me what i need to do to show out of stock next to variations if product is out of stock for real, because i m using radio buttons as replacement for standard dropdown button from Woocommerce.
Seems over complicated, I managed to do it this way. Seems to work, displays out of stock for the correct variations when I tested it.
add_filter( 'woocommerce_variation_option_name', 'customizing_variations_terms_name', 10, 1 );
function customizing_variations_terms_name( $term_name ) {
global $product;
// Get available product variations
$product_variations = $product->get_available_variations();
// product_variation terms are normalized to lowercase with
// spaces replaced by dashes.
// if your term name contains capital letters or spaces i.e. 'SIZE 6'
// the product variation will be lowercase with dashes, i.e. 'size-6'
/*
$term_name = str_replace(' ', '-', strtolower($term_name));
*/
foreach ( $product_variations as $product_variation ) {
if( isset( $product_variation['attributes'] ) ) {
$key = array_search($term_name, $product_variation['attributes']);
if( $key !== false && ! $product_variation['is_in_stock'] ) {
return $term_name . ' - Out of Stock';
}
}
}
return $term_name;
}
In addition to this you can grey out the product so they can't select it if that's something that are after.
add_filter( 'woocommerce_variation_is_active', 'grey_out_variations_when_out_of_stock', 10, 2 );
function grey_out_variations_when_out_of_stock( $grey_out, $variation ) {
if ( ! $variation->is_in_stock() )
return false;
return true;
}