Display Woocommerce product attribute on archive page - php

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".

Related

Add Term Meta from Custom Taxonomy to Cart Item

What I'm trying to accomplish is adding term meta from a custom taxonomy that is attached to a product and display it on the cart/checkout/ order details/email notification. I'm not the greatest at this but know enough to get by.
What I'm using is the 'Perfect Brands WooCommerce' plugin that adds in the "pwb-brand". I've also added in my own terms so we can show ETA leads times based on the brand. What I'm trying to have show up is like the following:
/**
* Add ETA for builder for
* Better user experience
*/
public function product_eta($eta) {
global $product;
$brands = wp_get_object_terms($product->get_id(), 'pwb-brand');
ob_start();
?>
<?php
if ( is_product() ) {
foreach ($brands as $brand) :
?>
<?php
$brand_eta_txt = get_option('wc_pwb_admin_tab_brand_single_product_eta_txt');
$eta_start = get_term_meta($brand->term_id, 'pwb_brand_eta_start', true);
$eta_end = get_term_meta($brand->term_id, 'pwb_brand_eta_end', true);
$eta_txt = get_term_meta($brand->term_id, 'pwb_brand_eta_txt', true);
?>
<div id="pwb-eta-content">
<?php
echo '<span class="avail"><strong>Availability: </strong> Estimated '.$eta_start.'-'.$eta_end.' weeks (' . date('m/d', strtotime('+'.$eta_start.' weeks')). '-' . date('m/d', strtotime('+'.$eta_end.' weeks')) . ')');
?>
</div>
<?php
endforeach;
}
?>
<?php
echo ob_get_clean();
return $eta;
}
This is a stripped down version of what we display on our product pages (https://25e62027b7.nxcli.net/shop/dining/extend-a-bench/brooklyn-extend-a-bench/). I'm trying to add the availability time that I added to the pwb-brand to each product item. I have the following in our functions.php at the moment:
add_filter( 'woocommerce_cart_item_name', function( $link_text, $cart_item, $cart_item_key ) {
$_product = $cart_item['data'];
$brands = implode(', ', wp_get_post_terms( $_product->get_id(), 'pwb-brand', ['fields' => 'names'] ) );
$link_text = '<div>' . __( 'Brand', 'perfect-woocommerce-brands' ) . ': ' . $brands . '</div>';
$product_permalink = apply_filters( 'woocommerce_cart_item_permalink', $_product->is_visible() ? $_product->get_permalink( $cart_item ) : '', $cart_item, $cart_item_key );
if ( ! $product_permalink ) {
$link_text .= $_product->get_name();
} else {
$link_text .= sprintf( '%s', esc_url( $product_permalink ), $_product->get_name() );
}
return $link_text;
}, 10, 3 );
// Display order items product brands (Orders on front end and emails)
add_action( 'woocommerce_order_item_meta_end', 'display_custom_data_in_emails', 10, 4 );
function display_custom_data_in_emails( $item_id, $item, $order, $bool ) {
// Get the product brands for this item
$terms = wp_get_post_terms( $item->get_product_id(), 'pwb-brand', array( 'fields' => 'names' ) );
// Output a coma separated string of product brand names
echo "<br><small>" . implode(', ', $terms) . "</small>";
}
// Display order items product brands in admin order edit pages
add_action( 'woocommerce_after_order_itemmeta', 'custom_admin_order_itemmeta', 15, 3 );
function custom_admin_order_itemmeta( $item_id, $item, $product ){
//if( ! is_admin() ) return; // only backend
// Target order "line items" only to avoid errors
if( $item->is_type( 'line_item' ) ){
// Get the product brands for this item
$terms = wp_get_post_terms( $item->get_product_id(), 'pwb-brand', array( 'fields' => 'names' ) );
// Output a coma separated string of product brands names
echo "<br><small>" . implode(', ', $terms) . "</small>";
}
}
I found these snippets while trying to figure this out and it's close to what I'm after however since it uses the "wp_get_post_terms" to pull I can not get anything past the name or id. Is there a way to pull the term meta from the 'pwb-brand' and have it displayed for each cart item in the cart/checkout/order details/email notification?
Try this for cart
add_filter( 'woocommerce_cart_item_name', function( $link_text, $cart_item, $cart_item_key ) {
$brands = wp_get_object_terms($cart_item['product_id'], 'pwb-brand');
$term_id = $terms[0]->term_id;
$eta_start = get_term_meta($term_id, 'pwb_brand_eta_start', true);
$eta_end = get_term_meta($term_id, 'pwb_brand_eta_end', true);
$eta_txt = get_term_meta($term_id, 'pwb_brand_eta_txt', true);
if ( $eta_start != "" ){
$link_text .= ' <br><span class="avail"><strong>Availability: </strong> Estimated '.$eta_start.'-'.$eta_end.' weeks (' . date('m/d', strtotime('+'.$eta_start.' weeks')). '-' . date('m/d', strtotime('+'.$eta_end.' weeks')) . ')';
}
return $link_text;
}, 10, 3 );
And this for email
add_action( 'woocommerce_order_item_meta_end', 'display_custom_data_in_emails', 10, 4 );
function display_custom_data_in_emails( $item_id, $item, $order, $bool ) {
// Get the product brands for this item
$terms = wp_get_post_terms( $item->get_product_id(), 'pwb-brand', array( 'fields' => 'names' ) );
$brands = wp_get_object_terms($cart_item['product_id'], 'pwb-brand');
$term_id = $terms[0]->term_id;
$eta_start = get_term_meta($term_id, 'pwb_brand_eta_start', true);
$eta_end = get_term_meta($term_id, 'pwb_brand_eta_end', true);
$eta_txt = get_term_meta($term_id, 'pwb_brand_eta_txt', true);
if ( $eta_start != "" ){
echo ' <br><span class="avail"><strong>Availability: </strong> Estimated '.$eta_start.'-'.$eta_end.' weeks (' . date('m/d', strtotime('+'.$eta_start.' weeks')). '-' . date('m/d', strtotime('+'.$eta_end.' weeks')) . ')';
}
}

Display Variable Product Attributes and Terms on Woocommerce Archives

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.

Add variation stock quantity and status to Woocommerce product variation dropdown

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"

Display product attribute only when product is in stock in Woocommerce

I have set up a Woocommerce attribute for delivery time and am displaying it on the single product page with this:
//Single Product
function product_attribute_delivery(){
global $product;
$taxonomy = 'pa_delivery';
$value = $product->get_attribute( $taxonomy );
if ( $value ) {
$label = get_taxonomy( $taxonomy )->labels->singular_name;
echo '<p>' . $label . ': ' . $value . '</p>';
}
}
add_action( 'woocommerce_single_product_summary', 'product_attribute_delivery', 25 );
Would it be possible to only display it when the product is in stock?
It is possible using WC_Product is_in_stock() method:
add_action( 'woocommerce_single_product_summary', 'product_attribute_delivery', 25 );
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 '<p>' . $label . ': ' . $value . '</p>';
}
}
Code goes in function.php file of your active child theme (or active theme). Tested and work

Show "Out Of Stock" next to product variation in Woocommerce

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';

Categories