Add Term Meta from Custom Taxonomy to Cart Item - php

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')) . ')';
}
}

Related

Display product categories and "brand" attribute term names under item name everywhere in WooCommerce

I want to display the category & attribute (brand) of each product under the product name in the Cart/checkout page
Example:
"Name of product"
"Category | Brand"
As shown in this image from shop
I would like to display it the same way on the cart page
(and also Thank you page + Order details, but these are lower priority)
I have this code which adds the Category to the cart page, but how can i add the attribute/brand next to it? Refer to this image
add_filter( 'woocommerce_cart_item_name', 'category_under_at_cart', 99, 3);
function category_under_at_cart( $name, $cart_item, $cart_item_key ) {
$product_item = $cart_item['data'];
// make sure to get parent product if variation
if ( $product_item->is_type( 'variation' ) ) {
$product_item = wc_get_product( $product_item->get_parent_id() );
}
$cat_ids = $product_item->get_category_ids();
$attributes = $product_item->get_attributes();
// if product has categories, concatenate cart item name with them
if ( $cat_ids ) $name .= '</br>' . wc_get_product_category_list( $product_item->get_id(), ', ', '<span class="posted_in">' . _n( count( $cat_ids )) . ' ', ' | ','</span>');
return $name;
}
The following will display the formatted product category(ies) and product attribute "brand" term names on minicart, cart, checkout, customer order and email notifications:
// Custom funtion that return the formatted category(ies) and attribute 'brand' term names
function get_categories_and_brand_html( $product_id ){
$product = wc_get_product($product_id);
$cat_names = (array) wp_get_post_terms( $product_id, 'product_cat', ['fields' => 'names'] );
$brand_name = $product->get_attribute('brand');
$output = '';
if ( ! empty($cat_names) || ! empty($brand_name) ) {
$output .= '</br><span class="posted_in">';
if ( ! empty($cat_names) ) {
$output .= implode(', ', $cat_names);
}
if ( ! empty($cat_names) && ! empty($brand_name) ) {
$output .= ' | ';
}
if ( ! empty($brand_name) ) {
$output .= $brand_name;
}
$output .= '</span>';
}
return $output;
}
// Display term names in minicart and cart page
add_filter( 'woocommerce_cart_item_name', 'category_brand_after_cart_item_name', 100, 3 );
function category_brand_after_cart_item_name( $item_name, $cart_item, $cart_item_key ) {
$terms_html = get_categories_and_brand_html( $cart_item['product_id'] );
if ( ! is_checkout() && ! empty($terms_html) ) {
$item_name .= $terms_html;
}
return $item_name;
}
// Display term names in checkout page
add_filter( 'woocommerce_checkout_cart_item_quantity', 'category_brand_after_checkout_item_name', 100, 3 );
function category_brand_after_checkout_item_name( $quantity, $cart_item, $cart_item_key ) {
$terms_html = get_categories_and_brand_html( $cart_item['product_id'] );
if ( is_checkout() && ! empty($terms_html) ) {
$quantity .= $terms_html;
}
return $quantity;
}
// Display term names on customer orders
add_filter( 'woocommerce_order_item_quantity_html', 'category_brand_after_order_item_name', 100, 2 );
function category_brand_after_order_item_name( $item_name, $item ) {
$terms_html = get_categories_and_brand_html( $item->get_product_id() );
if ( is_wc_endpoint_url() && ! empty($terms_html) ) {
$item_name .= $terms_html;
}
return $item_name;
}
// Display term names on email notifications
add_filter( 'woocommerce_order_item_name', 'category_brand_after_email_item_name', 100, 3 );
function category_brand_after_email_item_name( $item_name, $item, $is_visible ) {
$terms_html = get_categories_and_brand_html( $item->get_product_id() );
if ( ! is_wc_endpoint_url() && ! empty($terms_html) ) {
$item_name .= $terms_html;
}
return $item_name;
}
Code goes in functions.php file of your active child theme (or active theme). Tested and works.

Display Woocommerce product attribute on archive page

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

Display terms for multiple Woocommerce custom taxonomies

I need an imploded list of terms from three custom Woocommerce taxonomies including the taxonomy name to display in the product loop. I'll also need to be able to display multiple terms for each taxonomy. However, I can only get it to work with one taxonomy. And I can't figure out how to display the corresponding taxonomy name.
My custom taxonomies are 'artists', 'illustrators' and 'authors'. Here's what I'm trying to accomplish:
Robert Douglas (Author), Bill Johnston (Illustrator), Kyle McBeth (Artist)
function list_author_terms() {
global $post;
$person = get_the_terms(get_the_ID(), 'authors', 'artists', 'illustrators');
if ( $person
&& !is_wp_error( $person )
) {
#usort( $person, function ( $a, $b )
{
return strcasecmp(
$a->slug,
$b->slug
);
});
// Display your terms as normal
$term_list = [];
foreach ( $person as $term )
$term_list[] = '' . esc_html( $term->name ) . '<span class="attribute"> (Author)</span> ';
$term_names[] = $term->name;
echo implode( ', ', $term_list);
echo '<br>';
}
}
Add follows code snippet to achieve your task -
add_action( 'woocommerce_after_shop_loop_item', 'list_author_terms', 6 );
function list_author_terms(){
$taxonomies = array( 'authors', 'artists', 'illustrators' );
$pro_list_terms = array();
foreach ( $taxonomies as $taxonomy ) {
$term_obj_list = get_the_terms( get_the_ID(), $taxonomy );
$tax_obj = get_taxonomy( $taxonomy );
if( $term_obj_list && ! is_wp_error( $term_obj_list ) ){
foreach ( $term_obj_list as $term ) {
$link = get_term_link( $term, $taxonomy );
$pro_list_terms[] = '' . $term->name . ' (' .$tax_obj->labels->singular_name . ')';
}
}
}
echo join( ', ', $pro_list_terms );
}

Auto add order item custom meta data to order note in WooCommerce

In WooCommerce, with this code, i'm using product custom fields to add some custom metadata to order items when making a purchase. I also need to auto add some of this custom metadata to the customer Order Notes (basically so it gets sent to my shipping fulfillment service).
Below is the code i'm currently using:
add_action('woocommerce_checkout_create_order', 'before_checkout_create_order', 20, 2);
function before_checkout_create_order( $order, $data ) {
$items_in_order=array();
foreach( $order->get_items() as $item_id => $item ) {
$product = $order->get_product_from_item( $item );
$text = $product->get_sku();
$bags_count = array(
'neutral_bags' => 0,
'bright_bags' => 0,
'pastel_bags' => 0,
'reg_tendons' => 0,
'gid_tendons' => 0
);
$meta_key1 = __('Neutral Bags', 'woocommerce');
$meta_key2 = __('Bright Bags', 'woocommerce');
$meta_key3 = __('Pastel Bags', 'woocommerce');
$meta_key4 = __('Reg Tendons', 'woocommerce');
$meta_key5 = __('GID Tendons', 'woocommerce');
$bags_count['neutral_bags'] += wc_get_order_item_meta( $item_id, $meta_key1, true );
$bags_count['bright_bags'] += wc_get_order_item_meta( $item_id, $meta_key2, true );
$bags_count['pastel_bags'] += wc_get_order_item_meta( $item_id, $meta_key3, true );
$bags_count['reg_tendons'] += wc_get_order_item_meta( $item_id, $meta_key4, true );
$bags_count['gid_tendons'] += wc_get_order_item_meta( $item_id, $meta_key5, true );
$text .= ' | ' . $bags_count['neutral_bags'];
$text .= ' | ' . $bags_count['bright_bags'];
$text .= ' | ' . $bags_count['pastel_bags'];
$text .= ' | ' . $bags_count['reg_tendons'];
$text .= ' | ' . $bags_count['gid_tendons'];
array_push( $items_in_order , $text );
}
$text = implode('<br/>', $items_in_order );
// get customer note
$note = $order->get_customer_note();
// Merging existing customer note with bags count (if it exist)
$note = empty( $note ) ? $text : $note . '<br/>' . $text . ' V1';
// Set the new customer note before saving
$order->set_customer_note( $note );
}
As per the below screenshot, you'll see the data is saved to the product, but doesn't get pulled into the order notes section correctly.
Any ideas why this might be?
As this is related to your other question and my answer there, so the meta keys are a bit different:
neutral_bag_count meta key is replaced by: Neutral Bags,
bright_bag_count meta key is replaced by: Bright Bags.
Update: I have revisited completely all your existing code, avoiding repetitions, making it more effective and compact.
I have set your custom fields keys and labels in a function that I call in all other hooked functions and I use foreach loops everywhere now. If you want to change a label, you will do it just once in this utility function.
The new code:
// Utility function (keys + translatable labels)
function bags_and_sheets_labels_keys(){
$domain = 'woocommerce';
return array(
'neutral_bag' => __('Neutral Bags', $domain),
'bright_bag' => __('Bright Bags', $domain),
'pastel_bag' => __('Pastel Bags', $domain),
'reg_tendon' => __('Regular Tendon Sheets', $domain),
'gid_tendon' => __('GID Tendon Sheets', $domain),
);
}
// Add custom fields to single product pages
add_action( 'woocommerce_before_add_to_cart_button', 'add_custom_fields_single_product', 20 );
function add_custom_fields_single_product(){
global $product;
$bags = bags_and_sheets_labels_keys();
?>
<div class="product-custom-fields">
<?php foreach($bags as $key => $value ): ?>
<input type="text" placeholder="<?php echo $value; ?>" name="<?php echo $key; ?>">
<?php endforeach; ?>
</div>
<div class="clear"></div>
<?php
}
// Save custom fields to cart object
add_filter( 'woocommerce_add_cart_item_data', 'save_custom_fields_cart_item_data', 10, 2 );
function save_custom_fields_cart_item_data( $cart_item_data, $product_id ){
$bags = bags_and_sheets_labels_keys();
foreach($bags as $key => $value ):
if(isset($_POST[$key]))
$cart_item_data['custom_data'][$key] = sanitize_text_field($_POST[$key]);
endforeach;
$cart_item_data['custom_data']['unique_key'] = md5( microtime().rand() );
WC()->session->set( 'custom_data', $cart_item_data['custom_data'] );
return $cart_item_data;
}
// Display custom fields in cart items (cart and checkout pages)
add_filter( 'woocommerce_get_item_data', 'display_custom_fields_cart_item_data', 10, 2 );
function display_custom_fields_cart_item_data($item_data, $cart_item){
if( ! array_key_exists( 'custom_data', $cart_item ) )
return $item_data;
$bags = bags_and_sheets_labels_keys();
foreach($bags as $key => $value ):
if( array_key_exists( $key, $cart_item['custom_data'] ) )
$item_data[] = array(
'key' => $value,
'value' => $cart_item['custom_data'][$key]
);
endforeach;
return $item_data;
}
// Save and display custom fields in order item meta
add_action( 'woocommerce_add_order_item_meta', 'add_custom_fields_order_item_meta', 20, 3 );
function add_custom_fields_order_item_meta( $item_id, $cart_item, $cart_item_key ) {
$bags = bags_and_sheets_labels_keys();
foreach($bags as $key => $meta_key_label ):
if( array_key_exists($key, $cart_item['custom_data']) )
wc_update_order_item_meta( $item_id, $meta_key_label, $cart_item['custom_data'][$key] );
endforeach;
}
// Add products SKUs and cutom fields as formated data in customer note
add_action('woocommerce_checkout_update_order_meta', 'after_checkout_create_order', 20, 2);
function after_checkout_create_order( $order_id, $data ) {
$skus = $counts = $output = array();
$bags = bags_and_sheets_labels_keys();
// Get the WC_Order Object (instance)
$order = wc_get_order($order_id);
// Loop through order items
foreach( $order->get_items() as $item_id => $item ) {
$product = $item->get_product();
$skus[] = $product->get_sku(); // Set SKUs in an array
// Add metakeys to the array (if they exist)
foreach( $bags as $meta_key_label ){
$item_meta = wc_get_order_item_meta( $item_id, $meta_key_label, true );
if( ! empty($item_meta) ){
$counts[$meta_key_label] += $item_meta;
$count = true;
}
}
}
// Add Products SKUs
$text = __('Products SKUs', 'woocommerce') . ': ' . implode(' - ', $skus ). ' | ';
// Get each custom item meta data count (if they exist)
if( isset($count)){
foreach( $counts as $key_label => $value ){
$output[] = $key_label . __(' count: ', 'woocommerce') . $value;
}
// Format all data as a text string
$text .= implode(' - ', $output );
}
// Get customer note
$note = $order->get_customer_note();
// Merging existing customer note with bags count (if it exist)
$note = empty( $note ) ? $text : $note . '<br/>' . $text;
// Set the new customer note before saving
$order->set_customer_note( $note );
$order->save();
}
Code goes in function.php file of the active child theme (or active theme).
Tested and works as expected.

PHP Woocomerce get product brand name - short description

I'm trying to put the tag in the short description of the product, but with the code I'm using the product sku is also showing up, and it's taking the link from the brand.
I just wanted the brand, and when I click go right to her page. what am I doing wrong?
add_filter( 'woocommerce_short_description', 'single_product_short_description', 10, 1 );
function single_product_short_description( $post_excerpt ){
global $product;
if ( is_single( $product->id ) )
$post_excerpt = '<div class="product-message"><p>' . __( "Vendido e entregue por", "woocommerce" ) . '</p></div>' . $post_excerpt;
if ( class_exists( 'WooCommerce' ) ) {
$kode = $product->get_sku();
$tax = array(
'Brand:'=>'product_brand',
);
foreach ($tax as $mk_label => $mk_value) {
$make_array = wp_get_post_terms(get_the_ID(), $mk_value, array("fields" => "names"));
foreach ($make_array as $value) {
$mk .= $value.", ";
}
echo '<strong>'.$mk_label.' '.rtrim($mk,", ").' '.$kode.'</strong>';
$mk ="";
}
}
return $post_excerpt;
}

Categories