I want to make some modification of my site, but for some reason, can get it. i tryed to use this function that found into this thread of Stack :
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 for some reason when insert into functions, show me "outofstock" next to every variation. I dont use standard Woocommerce dropdown variation style, and use WC Variations Radio Buttons to show radio buttons next to each variation, instead of dropdown. The problem is that i want to show only "Out Of Stock" , next to each variation. not "In Stock", so customer will know that wanted variation is not in stock before press Add to Cart button. SO seems that issue is this code:
if( $stock_qty>0 )
return $term_name .= ' - ' . $stock_status . ' ('.$stock_qty.')';
else
return $term_name .= ' - ' . $stock_status;
}
function looks like return
return $term_name .= ' - ' . $stock_status;
all the time. Any help ? Preview from my issue can be seen here. Thanks.
EDIT: This is the code from variable.php Radio Button Plugin where print variations as radio buttons:
<td class="value">
<?php
if ( ! empty( $options ) ) {
if ( taxonomy_exists( $name ) ) {
// Get terms if this is a taxonomy - ordered. We need the names too.
$terms = wc_get_product_terms( $product->get_id(), $name, array( 'fields' => 'all' ) );
foreach ( $terms as $term ) {
if ( ! in_array( $term->slug, $options ) ) {
continue;
}
print_attribute_radio( $checked_value, $term->slug, $term->name, $sanitized_name );
}
} else {
foreach ( $options as $option ) {
print_attribute_radio( $checked_value, $option, $option, $sanitized_name );
}
}
}
echo end( $attribute_keys ) === $name ?
apply_filters( 'woocommerce_reset_variations_link', '<a
class="reset_variations" href="#">' . __( 'Clear', 'woocommerce' ) . '</a>'
) : '';
?>
</td>
Maybe need to update something there to show properly?
do this instead
if( $stock_qty>0 )
//return $term_name .= ' - ' . $stock_status . ' ('.$stock_qty.')';
return $term_name;
else
//return $term_name .= ' - ' . $stock_status;
return $term_name .= ' - Out Of Stock';
Related
I have products with multiple attributes allowing for multiple dimensions of variations. I'd like to show the price difference in the dropdown, but it is not comparing the absolute minimum price, for each option.
This is the code I am using:
add_filter( 'woocommerce_variation_option_name','display_price_in_variation_option_name');
function display_price_in_variation_option_name( $term ) {
global $product;
if ( empty( $term ) ) {
return $term;
}
if ( empty( $product->id ) ) {
return $term;
}
$variation_id = $product->get_children();
$price_min = $product->get_variation_regular_price();
foreach ( $variation_id as $id ) {
$_product = new WC_Product_Variation( $id );
$variation_data = $_product->get_variation_attributes();
foreach ( $variation_data as $key => $data ) {
if ( $data == $term ) {
$html = $term;
$price_diff = $_product->get_price() - $price_min;
$price_html = '';
if ($price_diff > 0 ) { $price_html = ' (+£' . number_format((float)$price_diff, 2, '.', '') . ')';}
$html .= $price_html;
return $html;
}
}
}
return $term;
}
However, this is only showing the minimum price of the current attribute, not the product as a whole.
I hope this is clear - ideally in the above image for options that do not add cost, $price_html should be blank. The code works perfectly for variations with a single attribute option, but does not work for variations with multiple attribute options.
Your code can be simplified and optimized, but it can't work with multiple attribute dropdowns. It can only work if there is a unique attribute dropdown (see the alternative way after).
So this code works only with one attribute dropdown:
add_filter( 'woocommerce_variation_option_name','display_diff_price_in_attribute_option_name', 10, 4 );
function display_diff_price_in_attribute_option_name( $term_name, $term, $attribute, $product ) {
if ( ! is_a($product, 'WC_Product') || is_admin() ) {
return $term_name;
}
$price_min = $product->get_variation_regular_price('min', true); // Min price for display
foreach ( $product->get_children() as $variation_id ) {
$variation = wc_get_product($variation_id);
$term_value = $variation->get_attribute($attribute);
if ( $term_value == $term_name ) {
$price_diff = wc_get_price_to_display($variation) - $price_min;
if ($price_diff > 0 ) {
$term_name .= ' (+' . strip_tags( wc_price( $price_diff ) ) . ')';
}
break;
}
}
return $term_name;
}
So you need something different (when there is multiple attribute dropdowns).
The following code will display the price difference for the select variation, for example at the right of the selected variation price:
add_filter( 'woocommerce_available_variation', 'filter_available_variation_attributes', 10, 3 );
function filter_available_variation_attributes( $data, $product, $variation ){
$price_min = $product->get_variation_regular_price('min', true); // Min price for display
$price_diff = $data['display_price'] - $price_min;
if ($price_diff > 0 ) {
$data['price_html'] = '<span class="price">' . $variation->get_price_html() . ' <em>(+' . wc_price( $price_diff ) . ')</em></span>';
}
return $data;
}
Code goes in functions.php file of the active child theme (or active theme). Tested and works.
I have variable products with many variations where only a few items are actually In Stock while the majority of other variations are ''available on backorder''
I would like to be able to display a quick list of ONLY the items that are IN STOCK in the short product description of each product page so the customer doesn't have to try all variations one-by-one to finally find out which ones are in stock.
I've searched for plugins or code that can do this but did not find anything.
The closest code I found is:
add_action( 'woocommerce_after_shop_loop_item', 'bb_echo_stock_variations_loop' );
function bb_echo_stock_variations_loop(){
global $product;
if ( $product->get_type() == 'variable' ) {
foreach ( $product->get_available_variations() as $key ) {
$attr_string = array();
foreach ( $key['attributes'] as $attr_name => $attr_value ) {
$attr_string[] = $attr_value;
}
if ( $key['max_qty'] > 0 ) {
echo '<br/>' . implode( ', ', $attr_string ) . ': ' . $key['max_qty'] . ' in stock';
} else {
echo '<br/>' . implode(', ', $attr_string ) . ': out of stock';
}
}
}
}
But it displays "In stock" available variations on the SHOP page and I want it to be displayed on the single product short description.
How can I display "In stock" available variations in single product short description?
To display in stock variations list in product single pages on short description, use the following:
add_filter( 'woocommerce_short_description', 'display_in_stock_variations_to_short_description' );
function display_in_stock_variations_to_short_description( $excerpt ){
global $product;
if ( ! is_product() || empty($product) || ! is_a( $product, 'WC_Product' ) )
return $excerpt;
if( $product->is_type('variable') ) {
// Loop through visible children
foreach( $product->get_children() as $variation_id ) {
$variation = wc_get_product( $variation_id );
// Hide out of stock variations if 'Hide out of stock items from the catalog' is checked.
if ( ! $variation || ! $variation->exists() || ( 'yes' === get_option( 'woocommerce_hide_out_of_stock_items' ) && ! $variation->is_in_stock() ) ) {
continue;
}
// Filter 'woocommerce_hide_invisible_variations' to optionally hide invisible variations (disabled variations and variations with empty price).
if ( apply_filters( 'woocommerce_hide_invisible_variations', true, $product->get_id(), $variation ) && ! $variation->variation_is_visible() ) {
continue;
}
$max_qty = 0 < $variation->get_max_purchase_quantity() ? $variation->get_max_purchase_quantity() : $variation->get_stock_quantity();
$term_names = []; // Initializing
// Loop through variation attributes for current varation
foreach ( $variation->get_variation_attributes() as $attribute => $term_slug ) {
// Set the term name in an array
$term_names[] = ucfirst( str_replace( ['-', '_'],[' ', ' '], $term_slug ) );
}
if ( $max_qty > 0 ) {
$excerpt .= sprintf( '<br/>%s: %s %s',
implode(', ', $term_names),
$max_qty,
__('in stock', 'woocommerce')
);
}
}
}
return $excerpt;
}
// Avoid additional content from product short description to be displayed in variation description
add_filter( 'woocommerce_available_variation', 'filter_wc_available_variation_desscription', 10, 3);
function filter_wc_available_variation_desscription( $data, $product, $variation ) {
$max_qty = 0 < $variation->get_max_purchase_quantity() ? $variation->get_max_purchase_quantity() : $variation->get_stock_quantity();
if( $max_qty > 0 )
$data['variation_description'] = get_post_meta( $variation->get_id(), '_variation_description', true );
return $data;
}
Code goes in functions.php file of your active child theme (or active theme). Tested and works.
I have set up an attribute for my products for a delivery time. And I am using the following functions to display it on product archives, on single product pages, on Orders and emails notifications:
add_action( 'woocommerce_single_product_summary', 'product_attribute_delivery', 27 );
function product_attribute_delivery(){
global $product;
$taxonomy = 'pa_delivery';
$value = $product->get_attribute( $taxonomy );
if ( $value && $product->is_in_stock() ) {
$label = get_taxonomy( $taxonomy )->labels->singular_name;
echo '<small>' . $label . ': ' . $value . '</small>';
}
}
add_action('woocommerce_order_item_meta_end', 'custom_item_meta', 10, 4 );
function custom_item_meta($item_id, $item, $order, $plain_text)
{ $productId = $item->get_product_id();
$product = wc_get_product($productId);
$taxonomy = 'pa_delivery';
$value = $product->get_attribute($taxonomy);
if ($value) {
$label = get_taxonomy($taxonomy)->labels->singular_name;
echo '<small>' . $label . ': ' . $value . '</small>';
}
}
add_action( 'woocommerce_after_shop_loop_item', 'product_attribute_delivery_shop', 1 );
function product_attribute_delivery_shop(){
global $product;
$taxonomy = 'pa_delivery';
$value = $product->get_attribute( $taxonomy );
if ( $value && $product->is_in_stock() ) {
$label = get_taxonomy( $taxonomy )->labels->singular_name;
echo '<small>' . $label . ': ' . $value . '</small>';
}
}
I have two questions:
Is there a way o combine these functions to optimize and clean up the code?
For the archive page (but not the single product page!) I want the text to change when the product is not on stock. Instead of not being displayed at all, I would like it to be "Sold Out".
Note that the rule on StackOverFlow is one question at the time. You can use a custom function that you will call on each hooked function like:
// Custom function that handle the code to display a product attribute
function custom_display_attribute( $product, $taxonomy = 'pa_delivery') {
$value = $product->get_attribute( $taxonomy );
if ( ! empty($value) && $product->is_in_stock() ) {
$label = wc_attribute_label( $taxonomy );
echo '<small>' . $label . ': ' . $value . '</small>';
}
}
// On product archive pages
add_action( 'woocommerce_after_shop_loop_item', 'product_attribute_delivery_archives', 1 );
function product_attribute_delivery_archives() {
global $product;
custom_display_attribute( $product );
// When product is out of stock displays "Sold Out"
if ( ! $product->is_in_stock() ) {
echo __("Sold Out", "woocommerce");
}
}
// On product single pages
add_action( 'woocommerce_single_product_summary', 'product_attribute_delivery_single', 27 );
function product_attribute_delivery_single() {
global $product;
custom_display_attribute( $product );
}
// On orders and email notifications
add_action('woocommerce_order_item_meta_end', 'custom_item_meta', 10, 4 );
function custom_item_meta( $item_id, $item, $order, $plain_text ) {
custom_display_attribute( wc_get_product( $item->get_product_id() ) );
}
It should works.
On archive pages only when product is not in stock, it will displays "Sold Out".
I am trying to accomplish a attribute and term list on the shop page using the hook woocommerce_shop_loop_item_title. The goal is to get the attribute(s) and term(s) for the product and then to display it like this example:
Color: Red, Blue, Green
Size: Small, Medium, Large
Dimensions: 90*90, 100*100 and 120*120
but without the spaces between the rows.
It should "fetch" all the attributes used with the product and the attributes terms.
I've tried this but got fatal error.
add_action( 'woocommerce_shop_loop_item_title', 'variable_att_and_terms_on_loop');
function variable_att_and_terms_on_loop() {
foreach( $product->get_variation_attributes() as $taxonomy => $terms_slug ) {
$taxonomy_label = wc_attribute_label( $taxonomy, $product );
foreach($terms_slug as $term) {
$term_name = get_term_by('slug', $term, $taxonomy)->name;
$attributes_and_terms_names[$taxonomy_label][$term] = $term_name;
}
}
foreach ( $attributes_and_terms_names as $attribute_name => $terms_name ) {
$terms_string = implode( ', ', $terms_name );
echo '<p>' . $attribute_name . ': ' . $terms_string . '</p>';
}
}
I've also tried this:
add_action('woocommerce_shop_loop_item_title','add_attribute', 5);
function add_attribute() {
global $product;
$product_attributes = array( 'pa_weight', 'pa_quantity', 'pa_length', 'pa_color' );
$attr_output = array();
foreach( $product_attributes as $taxonomy ){
if( taxonomy_exists($taxonomy) ){
$label_name = get_taxonomy( $taxonomy )->labels->singular_name;
$value = $product->get_attribute('pa_weight');
if( ! empty($value) ){
$attr_output[] = '<span class="'.$taxonomy.'">'.$label_name.': '.$value.'</span>';
}
}
}
echo '<div class="product-attributes">'.implode( '<br>', $attr_output ).'</div>';
}
without any result.
After trying the new result below from LoicTheAztec, this is what I get:
Uppdate 2020 - Removed an error when trying to get the term name from a term slug.
In your first code snippet there are some mistakes:
the $product variable was not defined
The function needed to be restricted to variable products only
the $attributes_and_terms_names variable was not initialized…
Here is the revisited code (without the spaces between the rows):
add_action( 'woocommerce_shop_loop_item_title', 'variable_att_and_terms_on_loop');
function variable_att_and_terms_on_loop() {
global $product;
if( ! $product->is_type('variable') ) return; // Only for variable products
$variation_attributes = $product->get_variation_attributes();
if( sizeof($variation_attributes ) == 0 ) return; // Exit if empty
$attributes = array(); // Initializing
foreach( $product->get_variation_attributes() as $taxonomy => $terms_slug ) {
$taxonomy_label = wc_attribute_label( $taxonomy, $product );
$terms_name = array();
foreach($terms_slug as $term_slug ) {
// We try to get the term name when it's a term slug
$term = get_term_by('slug', $term_slug, $taxonomy);
$terms_name[] = ! is_a($term, 'WP_Term') ? $term_slug : $term->name;
}
$attributes[] = $taxonomy_label . ': ' . implode( ', ', $terms_name );
}
echo '<div class="product-attributes">';
echo '<span>' . implode('</span><br><span>', $attributes) . '</span>';
echo '</div>';
}
Code goes in function.php file of your active child theme (active theme). Tested and works.
Based on "https://stackoverflow.com/questions/45037405/show-stock-status-next-to-each-attribute-value-in-woocommerce-variable-products/45041602#45041602", I have the following code that shows stock quantity + stock status in product variation dropdown and also as displayed product availability text:
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 . ' (Vyprodáno)';
}
add_filter( 'woocommerce_get_availability', 'custom_get_availability', 1, 2);
function custom_get_availability( $availability, $_product ) {
global $product;
$stock = $product->get_total_stock();
if ( $_product->is_in_stock() ) $availability['availability'] = __($stock . ' Skladem', 'woocommerce');
if ( !$_product->is_in_stock() ) $availability['availability'] = __('Vyprodáno', 'woocommerce');
return $availability;
}
But I am having an issue with this code:
for ex. I have a product with size (stock): S (instock qty 2), L(0), XL(0).
When I select variation S - it shows Quantity 2 - that is correct, but this same quantity is shown even when I select variation L or XL. - that is wrong because they are on ZERO.
You can see it here: https://dogworld.cz/produkt/pelisek-pro-psa-reedog-beige-paw/
There are some mistakes in your code and a better way to shows stock quantity + stock status in product variation dropdown.
The first function is a custom function where you will define the stock text addition to be displayed in the product variation dropdown, which is handled by the second function.
In your last function, since Woocommerce 3, get_total_stock() is deprecated and replaced by the method get_stock_quantity(). Also you need to use the variation $product object that is included as an argument in the hooked function.
Note: This will only work for variable products with one dropdown (one defined product attribute for variations)
Here is the revisited code:
// Function that will check the stock status and display the corresponding additional text
function get_variation_stock_text( $product, $name, $term_slug ){
foreach ( $product->get_available_variations() as $variation ){
if($variation['attributes'][$name] == $term_slug ){
$is_in_stock = $variation['is_in_stock'];
$stock_qty = get_post_meta($variation['variation_id'], '_stock', true);
}
}
$in_stock = ' ('.$stock_qty.' ' .__("Skladem", "woocommerce").')';
$out_of_stock = ' ('.__("Vyprodáno", "woocommerce").')';
return $is_in_stock == 1 ? $in_stock : $out_of_stock;
}
// The hooked function that will add the stock text to the dropdown options elements.
add_filter( 'woocommerce_dropdown_variation_attribute_options_html', 'show_stock_status_in_dropdown', 10, 2);
function show_stock_status_in_dropdown( $html, $args ) {
// Only if there is a unique variation attribute (one dropdown)
if( sizeof($args['product']->get_variation_attributes()) == 1 ) :
$options = $args['options'];
$product = $args['product'];
$attribute = $args['attribute']; // The product attribute taxonomy
$name = $args['name'] ? $args['name'] : 'attribute_' . sanitize_title( $attribute );
$id = $args['id'] ? $args['id'] : sanitize_title( $attribute );
$class = $args['class'];
$show_option_none = $args['show_option_none'] ? true : false;
$show_option_none_text = $args['show_option_none'] ? $args['show_option_none'] : __( 'Choose an option', 'woocommerce' );
if ( empty( $options ) && ! empty( $product ) && ! empty( $attribute ) ) {
$attributes = $product->get_variation_attributes();
$options = $attributes[ $attribute ];
}
$html = '<select id="' . esc_attr( $id ) . '" class="' . esc_attr( $class ) . '" name="' . esc_attr( $name ) . '" data-attribute_name="attribute_' . esc_attr( sanitize_title( $attribute ) ) . '" data-show_option_none="' . ( $show_option_none ? 'yes' : 'no' ) . '">';
$html .= '<option value="">' . esc_html( $show_option_none_text ) . '</option>';
if ( ! empty( $options ) ) {
if ( $product && taxonomy_exists( $attribute ) ) {
$terms = wc_get_product_terms( $product->get_id(), $attribute, array( 'fields' => 'all' ) );
foreach ( $terms as $term ) {
if ( in_array( $term->slug, $options ) ) {
// HERE Added the function to get the stock text
$stock_text = get_variation_stock_text( $product, $name, $term->slug );
$html .= '<option value="' . esc_attr( $term->slug ) . '" ' . selected( sanitize_title( $args['selected'] ), $term->slug, false ) . '>' . esc_html( apply_filters( 'woocommerce_variation_option_name', $term->name ) . $stock_text ) . '</option>';
}
}
} else {
foreach ( $options as $option ) {
$selected = sanitize_title( $args['selected'] ) === $args['selected'] ? selected( $args['selected'], sanitize_title( $option ), false ) : selected( $args['selected'], $option, false );
// HERE Added the function to get the stock text
$stock_text = get_variation_stock_text( $product, $name, $option );
$html .= '<option value="' . esc_attr( $option ) . '" ' . $selected . '>' .
esc_html( apply_filters( 'woocommerce_variation_option_name', $option ) . $stock_text ) . '</option>';
}
}
}
$html .= '</select>';
endif;
return $html;
}
// Change product availability text
add_filter( 'woocommerce_get_availability_text', 'filter_product_availability_text', 10, 2);
function filter_product_availability_text( $availability, $product ) {
$stock = $product->get_stock_quantity();
return $product->is_in_stock() ? $stock . ' ' . __("Skladem", "woocommerce") : __("Vyprodáno", "woocommerce");
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.
Based on "How to add variation stock status to Woocommerce product variation dropdown"