I have this code to display product attributes
add_action('woocommerce_single_product_summary', 'attributes', 30 );
function attributes() {
global $product;
$attributes_names = array('Size', 'Color', 'Material', 'Pieces');
$attributes_data = array();
foreach ( $attributes_names as $attribute_name ) {
if ( $value = $product->get_attribute($attribute_name) ) {
$attributes_data[] = $attribute_name . ': ' . $value;
}
}
if ( ! empty($attributes_data) ) {
echo '<div><p>' . implode( '</p>', $attributes_data ) . '</div>';
}
}
Instead of displaying the attribute's value, I want to display their description, so something like
$attributes_data[] = $attribute_name . ': ' . $term_description;
How can this be done?
You can use wc_get_product_terms to retrieve the terms for a product (including its attributes). The parameters of the function are the product ID, the taxonomy and the query args.
For example, you could do something like this:
function attributes() {
global $product;
$attributes_names = array( 'Size', 'Color', 'Material', 'Pieces' );
$attributes_data = array();
foreach ( $product->get_attributes() as $attribute => $value ) {
$attribute_name = wc_get_attribute( $value->get_id() )->name;
if ( ! in_array( $attribute_name, $attributes_names, true ) ) {
continue;
}
$values = wc_get_product_terms( $product->get_id(), $attribute, array( 'fields' => 'all' ) );
if ( $values ) {
$attribute_descriptions = array();
foreach ( $values as $term ) {
$attribute_descriptions[] = term_description( $term->term_id, $term->taxonomy );
}
$attributes_data[] = $attribute_name . ': ' . implode( ',', $attribute_descriptions );
}
}
if ( ! empty( $attributes_data ) ) {
echo '<div><p>' . implode( '</p>', $attributes_data ) . '</div>';
}
}
To note that the attribute description includes any HTML tags, line breaks, etc. That's why in my example above I stripped those, but of course it might not be necessary.
Related
I want to show attibutes from a simple product, in cart and checkout. Just like variable products does, but there is only one attibute. See image below:
Is this possible to achive with PHP?
I was thinking about something like using echo $product->get_attributes()
Add the follows code snippets to achieve your above task -
function modify_woocommerce_get_item_data( $item_data, $cart_item ) {
if( $item_data || $cart_item['data']->is_type( 'variation' ) ) return $item_data;
if ( $cart_item['data']->is_type( 'simple' ) ) {
$attributes = array_filter( $cart_item['data']->get_attributes(), 'wc_attributes_array_filter_visible' );
foreach ( $attributes as $attribute ) {
$values = array();
if ( $attribute->is_taxonomy() ) {
$attribute_taxonomy = $attribute->get_taxonomy_object();
$attribute_values = wc_get_product_terms( $cart_item['data']->get_id(), $attribute->get_name(), array( 'fields' => 'all' ) );
foreach ( $attribute_values as $attribute_value ) {
$value_name = esc_html( $attribute_value->name );
if ( $attribute_taxonomy->attribute_public ) {
$values[] = '' . $value_name . '';
} else {
$values[] = $value_name;
}
}
} else {
$values = $attribute->get_options();
foreach ( $values as &$value ) {
$value = make_clickable( esc_html( $value ) );
}
}
$item_data[] = array(
'key' => wc_attribute_label( $attribute->get_name() ),
'value' => apply_filters( 'woocommerce_attribute', wpautop( wptexturize( implode( ', ', $values ) ) ), $attribute, $values ),
);
}
}
return $item_data;
}
add_filter( 'woocommerce_get_item_data', 'modify_woocommerce_get_item_data', 99, 2 );
Codes goes to your active theme's functions.php
Yes you can. Please try adding a filter as bellow,
add_filter('woocommerce_cart_item_name', function($name, $cart_item) {
//has attributes
if ($cart_item['data']->is_type( 'simple' ) && $attributes = $cart_item['data']->get_attributes()) {
$name .= " - ";
foreach ($attributes as $att)
$name .= $att->get_name() . " : " . implode(',', $att->get_options());
}
return $name;
}, 10, 2);
I'm using this code to get the variable product options
$terms = wc_get_product_terms( $bundle_product_id, $name, array( 'fields' => 'all' ) );
foreach ( $terms as $term ) {
if ( !in_array( $term->slug, $options ) ) {
continue;
}
echo '<option value="' . esc_attr( $term->slug ) . '" ' . selected( sanitize_title( $selected_value ), sanitize_title( $term->slug ), false ) . '>' . apply_filters( 'woocommerce_variation_option_name', $term->name ) . '</option>';
}
It will only work if you have just one product attribute for variations set in the variable product (so only one dropdown).
If you have more than one dropdowns in your variable product, as the variations are a combination of the different product attributes values, it will not work logically.
So the following code will display the product variation price in a unique product attribute dropdown:
// Utility function to get the price of a variation from it's attribute value
function get_the_variation_price_html( $product, $name, $term_slug ){
foreach ( $product->get_available_variations() as $variation ){
if($variation['attributes'][$name] == $term_slug ){
return strip_tags( $variation['price_html'] );
}
}
}
// Add the price to the dropdown options items.
add_filter( 'woocommerce_dropdown_variation_attribute_options_html', 'show_price_in_attribute_dropdown', 10, 2);
function show_price_in_attribute_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'];
$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 ) ) {
// Get and inserting the price
$price_html = get_the_variation_price_html( $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 ) . ' ::: ' . $price_html ) . '</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 );
// Get and inserting the price
$price_html = get_the_variation_price_html( $product, $name, $term->slug );
$html .= '<option value="' . esc_attr( $option ) . '" ' . $selected . '>' . esc_html( apply_filters( 'woocommerce_variation_option_name', $option ) . ' ::: ' . $price_html ) . '</option>';
}
}
}
$html .= '</select>';
endif;
return $html;
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.
Here is my simplified solution to this that is working with WooCommerce 4.7.0. Since we know there is only one attribute for each variation, we can just get the first value to show the correct price for each.
/**
* Display price in variation options dropdown for products that have only one attribute.
*
* #param string $term Existing option term value.
* #return string $term Existing option term value or updated term value with price.
*/
function display_price_in_variation_options( $term ) {
$product = wc_get_product();
$id = $product->get_id();
if ( empty( $term ) || empty( $id ) ) {
return $term;
}
if ( $product->is_type( 'variable' ) ) {
$product_variations = $product->get_available_variations();
} else {
return $term;
}
foreach ( $product_variations as $variation ) {
if ( count( $variation['attributes'] ) > 1 ) {
return $term;
}
$attribute = array_values( $variation['attributes'] )[0];
if ( $attribute === $term ) {
$term .= ' (' . wp_strip_all_tags( wc_price( $variation['display_price'] ) ) . ')';
}
}
return $term;
}
add_filter( 'woocommerce_variation_option_name', 'display_price_in_variation_options' );
In Woocommerce, there is a block of code in variable.php that displays the variation options. I am needing to display the SKU of each individual variation in the list next to the title. Is there a way I can do this that is similar to $product->get_sku;?
This is the full block:
global $product;
$attribute_keys = array_keys( $attributes );
<?php foreach ( $attributes as $name => $options ) : ?>
<tr class="attribute-<?php echo sanitize_title($name); ?>">
<?php
$sanitized_name = sanitize_title( $name );
if ( isset( $_REQUEST[ 'attribute_' . $sanitized_name ] ) ) {
$checked_value = $_REQUEST[ 'attribute_' . $sanitized_name ];
} elseif ( isset( $selected_attributes[ $sanitized_name ] ) ) {
$checked_value = $selected_attributes[ $sanitized_name ];
} else {
$checked_value = '';
}
?>
<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 ) {
echo $product->get_sku; // This is where the variation SKU would go
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>
</tr>
<?php endforeach; ?>
This is where the Variation SKU would go within this section:
foreach ( $options as $option ) {
echo $product->get_sku; // This is where the variation SKU would go
print_attribute_radio( $checked_value, $option, $option,
$sanitized_name );
}
Thanks.
Replace your foreach with:
$product = new WC_Product_Variable( $post->ID );
$variations = $product->get_available_variations();
foreach ($variations as $key => $variation) {
$sku = $variation['sku'];
$options = $variation['attributes'];
.
.
.
I'm trying to set the attribute value names of a product attribute as classname for each <span> item.
So I can set eg material as product attribute. And then attach values like stone, paper etc.
How can I set stone and paper as a classname? Normally values are printed like -> Material: stone, paper. I want to set a classname so I can change stone and paper to a small image.
So in woocommerce/single-product/add-to-cart/simple.php I have added this:
<?php
if ( $attribute['is_taxonomy'] ) {
$values = wc_get_product_terms( $product->id, $attribute['name'], array( 'fields' => 'names' ) );
///////////////this is added//////////////////////////
if($attribute['name'] == 'pa_material'){ // I only want to set a classname if attribute name == material
foreach($values as $value){
$value_classname = $value; // how can I get the value name??
}
echo apply_filters( 'woocommerce_attribute', wpautop( wptexturize('<span class="lbl ' . $value_classname . '">'. implode( '</span><span class="lbl ' . $value . '">', $values ).'</span>' ) ), $attribute, $values );
////////////////////////////////////////////
} else{
echo apply_filters( 'woocommerce_attribute', wpautop( wptexturize( implode( ', ', $values ) ) ), $attribute, $values );
}
} else {
// Convert pipes to commas and display values
$values = array_map( 'trim', explode( WC_DELIMITER, $attribute['value'] ) );
echo apply_filters( 'woocommerce_attribute', wpautop( wptexturize( implode( ', ', $values ) ) ), $attribute, $values );
}
?>
Right now the classname returns blank obviously.
To get the attribute values assigned to the product for just the material attribute:
global $product;
//this will get the names
$values = wc_get_product_terms($product->id, 'pa_material', array( 'fields' => 'names' ) );
I have Woocommerce and Woocommerce Bookings. I want to add some more info on the cart, after a user added it there.
At the moment, I have the starting date of the booking and the duration, with a label.
<dl class="variation">
<dt class="variation-Booking"><font><font class="">Booking Date:</font></font></dt>
<dd class="variation-Booking"><p><font><font class="">August 11, 2015</font></font></p></dd>
<dt class="variation-Duration"><font><font>Duration:</font></font></dt>
<dd class="variation-Duration"><p><font><font>4 days</font></font></p></dd>
</dl>
I try to find where this code is created on the PHP files of Woocommerce, so I can either add more attributes that are available on the database (like ending date and SKU for example) and also change the HTML output.
In the cart.php file, there is this part of the code:
// Meta data
echo WC()->cart->get_item_data( $cart_item );
Then on class-wc-cart.php the above function:
public function get_item_data( $cart_item, $flat = false ) {
$item_data = array();
// Variation data
if ( ! empty( $cart_item['data']->variation_id ) && is_array( $cart_item['variation'] ) ) {
foreach ( $cart_item['variation'] as $name => $value ) {
if ( '' === $value )
continue;
$taxonomy = wc_attribute_taxonomy_name( str_replace( 'attribute_pa_', '', urldecode( $name ) ) );
// If this is a term slug, get the term's nice name
if ( taxonomy_exists( $taxonomy ) ) {
$term = get_term_by( 'slug', $value, $taxonomy );
if ( ! is_wp_error( $term ) && $term && $term->name ) {
$value = $term->name;
}
$label = wc_attribute_label( $taxonomy );
// If this is a custom option slug, get the options name
} else {
$value = apply_filters( 'woocommerce_variation_option_name', $value );
$product_attributes = $cart_item['data']->get_attributes();
if ( isset( $product_attributes[ str_replace( 'attribute_', '', $name ) ] ) ) {
$label = wc_attribute_label( $product_attributes[ str_replace( 'attribute_', '', $name ) ]['name'] );
} else {
$label = $name;
}
}
$item_data[] = array(
'key' => $label,
'value' => $value
);
}
}
// Other data - returned as array with name/value values
$other_data = apply_filters( 'woocommerce_get_item_data', array(), $cart_item );
if ( $other_data && is_array( $other_data ) && sizeof( $other_data ) > 0 ) {
foreach ( $other_data as $data ) {
// Set hidden to true to not display meta on cart.
if ( empty( $data['hidden'] ) ) {
$display_value = ! empty( $data['display'] ) ? $data['display'] : $data['value'];
$item_data[] = array(
'key' => $data['name'],
'value' => $display_value
);
}
}
}
// Output flat or in list format
if ( sizeof( $item_data ) > 0 ) {
ob_start();
if ( $flat ) {
foreach ( $item_data as $data ) {
echo esc_html( $data['key'] ) . ': ' . wp_kses_post( $data['value'] ) . "\n";
}
} else {
wc_get_template( 'cart/cart-item-data.php', array( 'item_data' => $item_data ) );
}
return ob_get_clean();
}
return '';
}
And the cart-item-data.php that is called above:
<dl class="variation">
<?php
foreach ( $item_data as $data ) :
$key = sanitize_text_field( $data['key'] );
?>
<dt class="variation-<?php echo sanitize_html_class( $key ); ?>"><?php echo wp_kses_post( $data['key'] ); ?>:</dt>
<dd class="variation-<?php echo sanitize_html_class( $key ); ?>"><?php echo wp_kses_post( wpautop( $data['value'] ) ); ?></dd>
<?php endforeach; ?>
</dl>
However, I am still unaware of how I can change the layout and add specific attributes from the database.
add_filter( 'woocommerce_get_item_data', 'wc_add_address_to_cart', 10, 2 );
function wc_add_address_to_cart( $other_data, $cart_item ) {
$post_data = get_post( $cart_item['product_id'] );
$post_data->post_author;
$vendor_id = $post_data->post_author;
echo '<br>';
$Add = 'Address: ';
$city = 'City: ';
$tel = 'Phone: ';
echo $test;
$user_id = $vendor_id;
// Get all user meta data for $user_id
$meta = get_user_meta( $user_id );
// Filter out empty meta data
$meta = array_filter( array_map( function( $a ) {
return $a[0];
}, $meta ) );
echo $Add;
print_r( $meta ['_vendor_address_1'] );
echo '<br>';
echo $city;
print_r( $meta['_vendor_city'] );
echo '<br>';
echo $tel;
print_r( $meta['_vendor_phone'] );
return $other_data;
}
Add this to the functions.php file in your theme. it will display the Address of each vendor on the products. you can customize it to what ever you want.