Change WooCommerce Product Name to Include Attribute When Attribute Selected - php

I've referenced the answer from this question which works perfectly!
But I am displaying a product on the homepage using a woocommerce shortcode and can't figure out how to adjust so it also applies to the homepage.
I tried adjusting the line that selects only product pages to include the homepage, but it does not work:
// Only single product pages
if( ! is_product() && ! is_home() ) return $title;
Any advice would be appreciated!

This only work on variable products that have a Color product attribute (for variations) and it will append the selected Color attribute label value to the product title.
For your Home page you will use WordPress conditional tag is_front_page()…
You will have to set also the Product ID that you are using in your shortcode.
1) In the Home page I have this shortcode example (with a variable product ID):
[product_page id="40"]
2) To make it work for both single product pages and the home page (product shortcode) too:
add_filter( 'wp_footer','custom_product_title_script' );
function custom_product_title_script(){
// Only single product pages and home page
if( ! ( is_product() || is_front_page() ) ) return;
// HERE Set your home product ID
$shortcode_product_id = 40; // <===== ===== ===== HERE your Shortcode Product ID
$product_id = is_product() ? get_the_id() : $shortcode_product_id;
// get an instance of the WC_Product Object
$product = wc_get_product( $product_id );
// Only for variable products
if( ! $product->is_type( 'variable' ) ) return;
// Here set your specific product attributes in this array (coma separated):
$attributes = array('pa_color');
// The 1st loop for variations IDs
foreach($product->get_visible_children( ) as $variation_id ) {
// The 2nd loop for attribute(s)/value
foreach($product->get_available_variation( $variation_id )['attributes'] as $key => $value_id ){
$taxonomy = str_replace( 'attribute_', '', $key ); // Get the taxonomy of the product attribute
// Just for defined attributes
if( in_array( $taxonomy, $attributes) ){
// Set and structure data in an array( variation ID => product attribute => term name )
$data[ $variation_id ][$taxonomy] = get_term_by( 'slug', $value_id, $taxonomy )->name;
}
}
}
?>
<script type="text/javascript">
(function($){
// variables initialization
var variationsData = <?php echo json_encode($data); ?>,
productTitle = $('.product_title').text(),
color = 'pa_color';
// function that get the selected variation and change title
function update_the_title( productTitle, variationsData, color ){
$.each( variationsData, function( index, value ){
if( index == $('input.variation_id').val() ){
$('.product_title').text(productTitle+' - '+value[color]);
return false;
} else {
$('.product_title').text(productTitle);
}
});
}
// Once all loaded
setTimeout(function(){
update_the_title( productTitle, variationsData, color );
}, 300);
// On live event: select fields
$('select').blur( function(){
update_the_title( productTitle, variationsData, color );
});
})(jQuery);
</script>
<?php
}
This code goes in any plugin file with a constructor (but not in function.php file).
Tested and works.

Related

How do I display price of selected bundle products instead of "From $"?

I have a product consisting of a bundle of 5 products. All of these products are optional but checked on default. Therefore the main product is displayed as "From 900 kr." instead of displaying the price of the selected items. I am using WooCommerces own plugin called "WooCommerce Product Bundles".
picture of the product page
The total price is already shown underneath the bundle options but I want that to be showed as the main price area.
I've already succeeded with this on variable product types and thought i could use the same code with a few changes. Sadly I couldn't make it work.
The code I used for the variable products:
// Show selected variation price in main price area
add_action('woocommerce_before_add_to_cart_form', 'selected_variation_price_replace_variable_price_range');
function selected_variation_price_replace_variable_price_range(){
global $product;
if( $product->is_type('variable') ):
?><style> .woocommerce-variation-price {display:none;} </style>
<script>
jQuery(function($) {
var p = 'p.price'
q = $(p).html();
$('form.cart').on('show_variation', function( event, data ) {
if ( data.price_html ) {
$(p).html(data.price_html);
}
}).on('hide_variation', function( event ) {
$(p).html(q);
});
});
</script>
<?php
endif;
}
Does anyone know how I can achieve this?
EDIT:
I've hidden the main price on the product page and styled the bundle price to look like the main price. The problem now lies on the category page where the price doesn't display the price of the default selected variation of the bundles. It shows the max possible price for the product and not the default bundle selection price.
Here's the code from the bundle plugin that results in a max price display:
/**
* 'woocommerce_bundle_force_old_style_price_html' filter.
*
* Used to suppress the range-style display of bundle price html strings.
*
* #param boolean $force_suppress_range_format
* #param WC_Product_Bundle $this
*/
if ( $suppress_range_price_html || apply_filters( 'woocommerce_bundle_force_old_style_price_html', false, $this ) ) {
$price = wc_price( $price_max );
$regular_price_min = $this->get_bundle_regular_price( 'max', true );
if ( $regular_price_min !== $price_max ) {
$regular_price = wc_price( $regular_price_max );
if ( $price_min !== $price_max ) {
$price = sprintf( _x( '%1$s%2$s', 'Price range: from', 'woocommerce-product-bundles' ), wc_get_price_html_from_text(), wc_format_sale_price( $regular_price, $price ) . $this->get_price_suffix() );
} else {
$price = wc_format_sale_price( $regular_price, $price ) . $this->get_price_suffix();
}
Since the price displays and effectively changes upon variation change within the product page that code must also be available to use for the shop loop I just can't seem to find it.
Right now you are only checking for product type variable. You have to check for bundle as well.
Simply you have to add more conidition with or $product->is_type('bundle')
Check the below code.
add_action('woocommerce_before_add_to_cart_form', 'selected_variation_price_replace_variable_price_range');
function selected_variation_price_replace_variable_price_range(){
global $product;
if( $product->is_type('variable') || $product->is_type('bundle') ):
?><style> .woocommerce-variation-price {display:none;} </style>
<script>
jQuery(function($) {
var p = 'p.price'
q = $(p).html();
$('form.cart').on('show_variation', function( event, data ) {
if ( data.price_html ) {
$(p).html(data.price_html);
}
}).on('hide_variation', function( event ) {
$(p).html(q);
});
});
</script>
<?php
endif;
UPDATE: try the below code. code will go in your active theme function.php file.
function override_woocommerce_bundle_default_variation_price_html( $price, $product ) {
if( $product->is_type('variable') || $product->is_type('bundle') ){
$bundled_items = $product->get_bundled_items();
if ( ! empty( $bundled_items ) ) {
foreach ( $bundled_items as $bundled_item ) {
foreach( $bundled_item->product->get_available_variations() as $pav ){
$def = true;
foreach( $bundled_item->product->get_variation_default_attributes() as $defkey => $defval ){
if( $pav['attributes']['attribute_'.$defkey] != $defval){
$def = false;
}
}
if( $def ){
$price = $pav['display_price'];
}
}
}
}
return wc_price($price);
}
return $price;
}
add_filter( 'woocommerce_get_price_html', 'override_woocommerce_bundle_default_variation_price_html', 10, 2 );

Set and display a custom product variation value dynamically on WooCommerce single products

I have a WooCommerce jewelry site. On single variable product page I have variations and I added a text box (text box is for the customer to write something that will be printed on the jewelry).
So I expect the page to work as follows: If the customer selects a particular product variation and starts typing in the textbox price of the product should change depending on number of letters (NOTE: this should only work if one specific variation is selected) on every other product price will be fixed.
I did manage to do the part for the textbox price update.
The issue I am having here is with the selection of specific product variation.
Based on Display the product attribute term for the selected variation in Woocommerce answer, I tried to solve this problem with the following code attempt:
function vendor_defined_taxonomy() {
return 'Materijal';
}
add_action( 'woocommerce_product_meta_end', 'display_product_vendors', 10 );
function display_product_vendors() {
$taxonomy = vendor_defined_taxonomy();
$term_ids = wp_get_post_terms( get_the_ID(), $taxonomy, array('fields' =>
'ids') );
if( sizeof($term_ids) > 0 ){
echo '<span class="posted_in vendors"></span>';
}
}
add_filter( 'woocommerce_available_variation', 'selected_variation_vendor_value', 10, 3 );
function selected_variation_vendor_value( $data, $product, $variation ) {
$taxonomy = vendor_defined_taxonomy();
if( isset($data['attributes']['attribute_'.$taxonomy]) )
$term = get_term_by( 'slug', $data['attributes']['attribute_'.$taxonomy], $taxonomy );
if( isset($term) && is_a($term, 'WP_Term' ) )
$data['variation_description'] .= '<input type="hidden" name="vendor- hidden" id="vendor-hidden" value="'.$term->name.'">';
return $data;
}
add_action('woocommerce_before_add_to_cart_button', 'custom_product_jquery_script');
function custom_product_jquery_script() {
global $product;
$taxonomy = vendor_defined_taxonomy();
$terms_string = $product->get_attribute($taxonomy);
if( ! empty($terms_string) ) :
?>
<script type="text/javascript">
jQuery(function($) {
var form = 'form.variations_form',
selected = 'input[name="variation_id"]',
vendorVal = 'input#vendor-hidden',
vendorTarget = 'span.vendors',
vendorHtml = $(vendorTarget).text(),
vendorLabel = '';
// On variation select
$(form).on( 'blur', 'select', function() {
if($(selected).val() != ''){
$(vendorTarget).text("");
if($(vendorVal).val() == 'Zlato'){
//$(vendorTarget).text(vendorLabel+'
'+$(vendorVal).val());
$(vendorTarget).text("here is your text");
}
}
});
});
</script>
<?php
endif;
}
But I can't make it work to print the "Matrijal" product attribute selected name value under product meta section.
Product attribute name is "Matrijal" (and slug "mats") … and for example a term name is "Zlato" (and slug "zlato").
Any help?
You are making things more complicated that they should be…
Here is a lightweight and effective way to add specific selected variation custom value (from specific product attribute) to an additional html '' after single product meta.
You just need to define the correct product attribute taxonomy in the first function.
The code:
// Add custom variation data to variation form data
add_filter( 'woocommerce_available_variation', 'add_variation_vendor_value', 10, 3 );
function add_variation_vendor_value( $data, $product, $variation ) {
// Here define the targeted taxonomy used in product variation
$taxonomy = 'pa_mats';
$term_name = $variation->get_attribute($taxonomy);
if( ! empty($term_name) ) {
$data['vendor_value'] = $term_name;
}
return $data;
}
// Display after product meta an empty span html on variable products
add_action( 'woocommerce_product_meta_end', 'display_product_vendors', 10 );
function display_product_vendors() {
global $product;
if ( $product->get_type() === 'variable' ) {
echo '<span class="posted_in vendors"></span>';
}
}
// Fill in our html "span" with specific text value from selected variation
add_action('woocommerce_after_variations_form', 'custom_product_variation_js');
function custom_product_variation_js() {
?>
<script type="text/javascript">
jQuery(function($) {
var $form = $('form.variations_form'),
$vendor = $('span.vendors'),
text = $vendor.text();
$form.on('show_variation', function(event, data){ // On selected variation
if ( data.vendor_value ) {
$vendor.text( data.vendor_value );
}
}).on('hide_variation', function(){ // Not on selected variation
$vendor.text( text );
});
});
</script>
<?php
}
Code goes in functions.php file of the active child theme (or active theme).
Tested and works on last WooCommerce version (4.9.2) on a variable product with a defined product attribute (for variations) taxonomy.
Remember that product attribute taxonomy always start with "pa_" + product attribute slug.

Add CSS to WooCommerce product pages only if product is not on sale

I need to add some CSS to hide some elements/classes on the page if the product is NOT on sale.
From another function I have, I am assuming I can check if $args['meta_key'] = '_sale_price'; is blank and if it then adds the CSS.
How can I use this in my functions.php?
add_action( 'wp', 'add_cssif_product_not_onsale' );
function add_cssif_product_not_onsale() {
if ( is_product() ) {
global $post;
$product = wc_get_product( $post );
if ( !$product->is_on_sale() )
echo "<style>body{display:none}</style>";
}
}

Change single add to cart text when product is out of stock in WooCommerce

In Woocommerce I am trying to change the add to cart text from "Add to cart" to "Out of stock" on single product pages when the product is out of stock, for simple products and the product variations on variable products.
We are currently not using any sort of stock management because all of our products are hand made & we do set stock status manually.
Is there any possible way to achieve that?
For all products except variable products, is quite simple… But for variable products and it's variations, it requires some more code and the usage of javascript/jQuery.
So the following code will change add to cart text to "Out of stock" when the current product is out of stock even for selected product variations on variable products:
// For all products except variable product
add_filter( 'woocommerce_product_single_add_to_cart_text', 'product_single_add_to_cart_text_filter_callback', 20, 2 );
function product_single_add_to_cart_text_filter_callback( $button_text, $product ) {
if( ! $product->is_in_stock() && ! $product->is_type('variable') ) {
$button_text = __("Out of stock", "woocommerce");
}
return $button_text;
}
// For all product variations (on a variable product)
add_action( 'woocommerce_after_add_to_cart_button', 'after_add_to_cart_button_action_callback', 0 );
function after_add_to_cart_button_action_callback() {
global $product;
if( $product->is_type('variable') ) :
$data = [];
// Loop through variation Ids
foreach( $product->get_visible_children() as $variation_id ){
$variation = wc_get_product( $variation_id );
$data[$variation_id] = $variation->is_in_stock();
}
$outofstock_text = __("Out of Stock", "woocommerce");
?>
<script type="text/javascript">
jQuery(function($){
var b = 'button.single_add_to_cart_button',
t = $(b).text();
$('form.variations_form').on('show_variation hide_variation found_variation', function(){
$.each(<?php echo json_encode($data); ?>, function(j, r){
var i = $('input[name="variation_id"]').val();
if(j == i && i != 0 && !r ) {
$(b).html('<?php echo $outofstock_text; ?>');
return false;
} else {
$(b).html(t);
}
});
});
});
</script>
<?php
endif;
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.
Tried to write as comment but seems code formatting not working, so writing as an answer.
For a single variation add to cart shortcode this hack will help.
// For variation product
add_filter( 'woocommerce_product_add_to_cart_text', 'product_variation_add_to_cart_text_filter_callback', 20, 2 );
function product_variation_add_to_cart_text_filter_callback( $button_text, $product ) {
if ( ! $product->is_in_stock() && $product->is_type( 'variation' ) ) {
$button_text = __( "Out of stock", "woocommerce" );
}
return $button_text;
}

Allow checkout only when a product of a mandatory category is in cart

I'd like to stop any customer advancing to the checkout if they do not have a particular product category in their basket. I would also like to tell them with an error message that they need to add a certain product. I've found some code but cannot it to work. I've added it as a code snippet into my Wordpress install but alas it does not function and there are no error messages even though I have debugging switched on. Here is the code that I have found in Github that may need modification in order for this to work:
function sv_wc_prevent_checkout_for_category() {
// set the slug of the category for which we disallow checkout
$category = 'sibling';
// 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;
}
$category_name = '' . $product_cat->name . '';
// check if this category is the only thing in the cart
if ( sv_wc_is_category_alone_in_cart( $category ) ) {
// render a notice to explain why checkout is blocked
wc_add_notice( sprintf( 'Hi there! Looks like your cart only contains products from the %1$s category – you must purchase a product from another category to check out.', $category_name ), 'error' );
}
}
add_action( 'woocommerce_check_cart_items', 'sv_wc_prevent_checkout_for_category' );
/**
* 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 sv_wc_is_category_alone_in_cart( $category ) {
// 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;
}
So I'm looking to stop checkout (with error message) if the 'sibling' category is the only item in the cart. I have a 'standard' category which must be in the basket before the customer makes it to the checkout. Hope this makes sense.
Here you have a solution that will make the trick. There is especially 2 main functions (the last ones):
The first function (N°3) display your message on cart page, when there is something in cart but not the mandatory product category. Displays also the message on the mandatory product archive pages (useful when customer get redirected from checkout, see below).
The second function (N°4) redirect customer to the product mandatory category archive pages when it tries to checkout and his cart has not that missing mandatory product category.
Define before your mandatory category slug in your_mandatory_category_slug() function.
This is the code:
// Function that define the mandatory product category
function your_mandatory_category_slug(){
// DEFINE HERE the SLUG of the needed product category
$category = 'clothing';
return $category;
}
// Conditional function that returns true if the mandatory product category is in cart
function has_mandatory_category(){
$category_needed = your_mandatory_category_slug();
$has_cat = false;
// Iterrating each item in cart and detecting…
foreach ( WC()->cart->get_cart() as $item ) {
// Detects if the needed product category is in cart items
if ( has_term($category_needed, 'product_cat', $item['product_id'] ) ) {
$has_cat = true;
break;
}
}
return $has_cat;
}
// Function that display a message if there is not in cart a mandatory product category
function mandatory_category_display_message() {
$category_needed = your_mandatory_category_slug();
// check that cart is not empty (for cart and product category archives)
if( !WC()->cart->is_empty() && ( is_cart() || is_product_category( $category_needed ) ) ){
$category_obj = get_term_by( 'slug', $category_needed, 'product_cat' );
if ( is_wp_error( $category_obj ) ) return;
// Display message when product category is not in cart items
if ( !has_mandatory_category() ) {
$category_name = $category_obj->name;
$category_url = get_term_link( $category_needed, 'product_cat' );
// render a notice to explain why checkout is blocked
wc_add_notice( sprintf( __( '<strong>Reminder:</strong> You have to add in your cart, a product from "%1$s" category, to be allowed to check out. Please return here to "%1$s" product page', 'your_theme_domain'), $category_name, $category_url ), 'error' );
}
}
}
add_action( 'woocommerce_before_main_content', 'mandatory_category_display_message', 30 ); // for product mandatory category archives pages
add_action( 'woocommerce_check_cart_items', 'mandatory_category_display_message' ); // for cat page
// Function that redirect from checkout to mandatory product category archives pages
function mandatory_category_checkout_redirect() {
// If cart is not empty on checkout page
if( !WC()->cart->is_empty() && is_checkout() ){
$category_needed = your_mandatory_category_slug();
// If missing product category => redirect to the products category page
if ( !has_mandatory_category() )
wp_redirect( get_term_link( $category_needed, 'product_cat' ) );
}
}
add_action('template_redirect', 'mandatory_category_checkout_redirect');
This goes in function.php file of your active child theme (or theme) or also in any plugin file.
This code is tested and fully functional.
I have adapted LoicTheAztec's answer to work with 2 categories. It seems to be working. I did not adapt the text has_mandatory_categorytext function, . If you can follow the code you can diy, but I was focusing on just functionality. There is another answer here but I preferred this answer and the other was not working for me.
// Function that define the 2 mandatory product categories cat1 & cat2
function your_mandatory_category_slug(){ $category = 'cat1'; return $category; }
function your_mandatory_category_slug2(){ $category = 'cat2'; return $category; }
// Conditional function that returns true if the mandatory product category is in cart
function has_mandatory_category1(){
$category_needed = your_mandatory_category_slug();
$has_cat = false;
foreach ( WC()->cart->get_cart() as $item ) {
if ( has_term($category_needed, 'product_cat', $item['product_id'] ) ) {
$has_cat = true;
break;
}
}
return $has_cat;
}
// Conditional function that returns true if the mandatory product category is in cart
function has_mandatory_category2(){
$category_needed = your_mandatory_category_slug2();
$has_cat = false;
foreach ( WC()->cart->get_cart() as $item ) {
if ( has_term($category_needed, 'product_cat', $item['product_id'] ) ) {
$has_cat = true;
break;
}
}
return $has_cat;
}
// Function that redirect from checkout to mandatory product category archives pages
function mandatory_category_checkout_redirect() {
// If cart is not empty on checkout page
if( !WC()->cart->is_empty() && is_checkout() ){
$category_needed = your_mandatory_category_slug();
$category_needed2 = your_mandatory_category_slug2();
if ( !has_mandatory_category1() )
wp_redirect( get_term_link( $category_needed, 'product_cat' ) );
if ( !has_mandatory_category2() )
wp_redirect( get_term_link( $category_needed2, 'product_cat' ) );
}
}
add_action('template_redirect', 'mandatory_category_checkout_redirect');
Works like a charm for my Wordpress installation with Woocommerce v2.6.12.
I would like to add two mandatory product-categories, maybe 3 or 4 in the future. I tried changing:
$category = 'slug1';
to
$category = 'slug1', 'slug2';
and
$category = array('slug1','slug2');
Without success. Is there a way to make this code work with multiple slugs?

Categories