How to add product description/content of single product page (not the short description) to WooCommerce new order email notification?
I need to know specific written description of my products as most of them are almost same.
As you are targeting a specific email notification, first we need to get the Email ID to target the "New Order" email notification. The only way is to get it before and to set the value in a global variable.
Then in a custom function hooked in woocommerce_order_item_meta_end action hook, we display the product description exclusively for New Order email notification.
Here is that code:
## Tested on WooCommerce 2.6.x and 3.0+
// Setting the email_is as a global variable
add_action('woocommerce_email_before_order_table', 'the_email_id_as_a_global', 1, 4);
function the_email_id_as_a_global($order, $sent_to_admin, $plain_text, $email ){
$GLOBALS['email_id_str'] = $email->id;
}
// Displaying product description in new email notifications
add_action( 'woocommerce_order_item_meta_end', 'product_description_in_new_email_notification', 10, 4 );
function product_description_in_new_email_notification( $item_id, $item, $order = null, $plain_text = false ){
// Getting the email ID global variable
$refNameGlobalsVar = $GLOBALS;
$email_id = $refNameGlobalsVar['email_id_str'];
// If empty email ID we exit
if(empty($email_id)) return;
// Only for "New Order email notification"
if ( 'new_order' == $email_id ) {
if( version_compare( WC_VERSION, '3.0', '<' ) ) {
$product_id = $item['product_id']; // Get The product ID (for simple products)
$product = wc_get_product($item['product_id']);
} else {
$product = $item->get_product();
if( $product->is_type('variation') ) {
$product = wc_get_product( $item->get_product_id() );
}
}
// Get the Product description (WC version compatibility)
if ( method_exists( $item['product'], 'get_description' ) ) {
$product_description = $product->get_description(); // for WC 3.0+ (new)
} else {
$product_description = $product->post->post_content; // for WC 2.6.x or older
}
// Display the product description
echo '<div class="product-description"><p>' . $product_description . '</p></div>';
}
}
This code goes in function.php file of your active child theme (or theme) or also in any plugin file.
This code is tested and works.
Code Update and Error explanations in woocommerce_order_item_meta_end action hook:
PHP Warning for woocommerce_order_item_meta_end (Mike Joley)
Related
How to add a custom text 'per item' based on product category on all WooCommerce emails?
Therefore I tried to modify Add a custom text for a particular product on specific email notification in Woocommerce and Display a custom text based on product category in WooCommerce Email answer code to my needs.
// Setting the email_is as a global variable
add_action('woocommerce_email_before_order_table', 'the_email_id_as_a_global', 1, 4);
function the_email_id_as_a_global($order, $sent_to_admin, $plain_text, $email ){
$GLOBALS['email_id_str'] = $email->id;
}
// Displaying product description in new email notifications
add_action( 'woocommerce_order_item_meta_end', 'product_description_in_new_email_notification', 10, 3 );
function product_description_in_new_email_notification( $product_cat, $cat, $order = null ){
// HERE define your targetted product CAT
$targeted_id = 'x';
// HERE define the text information to be displayed for the targeted product id
$text_information = __("There is an offer in this particular item", "woocommerce");
// Getting the email ID global variable
$refNameGlobalsVar = $GLOBALS;
$email_id = $refNameGlobalsVar['email_id_str'];
// If empty email ID we exit
if(empty($email_id)) return;
// Only for "New Order email notification" for your targeted product ID
if ( 'customer_completed_order' == $email_id && 'new_order' == $email_id
in_array( $targeted_id, array( $cat->get_product_cat(), $item->get_variation_id() ) ) ) {
// Display the text
echo '<div class="product-text" style="margin-top:10px"><p>' . $text_information . '</p></div>';
}
}
Unfortunately without the desired result. I would like to display custom text under an order item name for all products of a particular product category in all email notifications (this is per item, not per email). Any advice?
Some notes on your code attempt:
$product_cat and $cat do not exist as arguments for the woocommerce_order_item_meta_end hook
$cat->get_product_cat() does not exist and is incorrect
Use has_term() WordPress function to check if the current post has any of given terms
So you get:
// Setting global variable
function action_woocommerce_email_before_order_table( $order, $sent_to_admin, $plain_text, $email ) {
$GLOBALS['email_data'] = array(
'email_id' => $email->id, // The email ID (to target specific email notification)
'is_email' => true // When it concerns a WooCommerce email notification
);
}
add_action( 'woocommerce_email_before_order_table', 'action_woocommerce_email_before_order_table', 1, 4 );
// Displaying description
function action_woocommerce_order_item_meta_end( $item_id, $item, $order, $plain_text ) {
// Getting the custom 'email_data' global variable
$ref_name_globals_var = $GLOBALS;
// Isset & NOT empty
if ( isset ( $ref_name_globals_var ) && ! empty( $ref_name_globals_var ) ) {
// Isset
$email_data = isset( $ref_name_globals_var['email_data'] ) ? $ref_name_globals_var['email_data'] : '';
// NOT empty
if ( ! empty( $email_data ) ) {
// Specific categories: the term name/term_id/slug. Several could be added, separated by a comma
$categories = array( 108, 1, 'categorie-1' );
// The text information
$text_information = __( 'There is an offer in this particular item', 'woocommerce' );
// Specific email notifications: multiple statuses can be added, separated by a comma
$email_ids = array( 'new_order', 'customer_processing_order', 'customer_completed_order', 'customer_on_hold_order' );
// Targeting specific email notifications AND check if the current post has any of given terms
if ( in_array( $email_data['email_id'], $email_ids ) && has_term( $categories, 'product_cat', $item->get_product_id() ) ) {
// Display the text
echo '<p>' . $text_information . '</p>';
}
}
}
}
add_action( 'woocommerce_order_item_meta_end', 'action_woocommerce_order_item_meta_end', 10, 4 );
I am trying to add the shipping class under each product to the new order emails for both admin and customers in WooCommerce. This is my first time posting a question so please forgive me if there are formatting issues.
I used the code found here: https://wordpress.stackexchange.com/questions/291637/woocommerce-add-shipping-class-below-each-product-in-shopping-cart-page
That adds the shipping class below each product in the cart but I want to be able to show it on the order emails also.
I am just unsure of which objects to call to grab the data for each product in the new order email.
Here's the code I'm using:
add_action( 'woocommerce_order_item_meta_start', 'ts_order_item_meta_start', 10, 4 );
function ts_order_item_meta_start( $item_id, $item, $order, $plain_text, $item_name ){
$product = $cart_item['data']; // Get the WC_Product object instance
$shipping_class_id = $product->get_shipping_class_id(); // Shipping class ID
$shipping_class_term = get_term( $shipping_class_id, 'product_shipping_class' );
if( empty( $shipping_class_id ) )
return $item_name; // Return default product title (in case of)
$label = __( 'Shipping class', 'woocommerce' );
return $item_name . '<br>
<p class="item-shipping_class" style="margin:12px 0 0;">
<strong>' .$label . ': </strong>' . $shipping_class_term->name . '</p>';
}
I expected the shipping class to be listed below each product in the "New Order" email but currently adding this code returns an internal server error upon checkout.
The following will display The product shipping class name in Woocommerce "New Order" Email notification:
// Setting the email_is as a global variable
add_action('woocommerce_email_before_order_table', 'the_email_id_as_a_global', 1, 4);
function the_email_id_as_a_global($order, $sent_to_admin, $plain_text, $email ){
$GLOBALS['email_id_str'] = $email->id;
}
// Display Items shipping class name in New Order email notification
add_filter( 'woocommerce_order_item_name', 'custom_order_item_name', 10, 3 );
function custom_order_item_name( $item_name, $item, $is_visible ) {
// Targeting email notifications only
if( is_wc_endpoint_url() ) return $item_name;
// Get the WC_Product object (from order item)
$product = $item->get_product();
if( $shipping_class_id = $product->get_shipping_class_id() ){
// Getting the email ID global variable
$refNameGlobalsVar = $GLOBALS;
$email_id = $refNameGlobalsVar['email_id_str'];
// Only for New Order email notification
if( ! empty($email_id) && 'new_order' === $email_id ) {
$shipping_class_name = get_term( $shipping_class_id, 'product_shipping_class' )->name;
$item_name .= '<br><p class="item-shipping_class" style="margin:12px 0 0;">
<strong>' . __( 'Shipping class', 'woocommerce' ) . ': </strong>' . $shipping_class_name . '</p>';
}
}
return $item_name;
}
Code goes in functions.php file of your active child theme (or active theme). Tested and works.
In Woocommerce with Advanced Custom Fields plugin, we have added a custom field to products and this field value is specific to each product.
Now I am trying to add this custom field value to our Woocommerce order confirmation emails.
I have tried the following code with no success:
<?php
if ( version_compare( WC_VERSION, '3.0', '<' ) ) {
$order_id = int_val( $order->id ); // Older than 3.0
} else {
$order_id = int_val( $order->get_id() ); // 3.0+
}
$inst1 = get_field(‘how1’, $order_id );
if( $inst1 ){
echo '<p>' . $inst1 . '</p>';
}
?> with Advanced Custom Fields plugin
As your custom field is specific to "product" post type (but NOT to "order" post type) you need to get first the order items to get the product ID that you should use with ACF get_field() function this way:
<?php
foreach ( $order->get_items() as $item ) {
// Get the product ID
if ( version_compare( WC_VERSION, '3.0', '<' ) ) {
$product_id = $item['product_id']; // Older than 3.0
} else {
$product_id = $item->get_product_id(); // 3.0+
}
$inst1 = get_field( 'how1', $product_id );
if( $inst1 ){
echo '<p>' . $inst1 . '</p>';
}
}
<?
The custom field value will be displayed for each item in the order, as an order can have many items in it.
References:
How to get WooCommerce order details
Get Order items and WC_Order_Item_Product in Woocommerce 3
I have made a filter to update how order is displayed on woocommerce.
Basically I need the shop owner to be able to click the name of each product (linked now to the featured image) and also him to be able to see the URL (because the image file name is useful for them to track the product)
I need this to ONLY affect the NEW ORDER email sent to the shop owner.
My code placed in functions.php does update BUT in ALL emails and also order confirmation table at the website.
Question? How can I ONLY affect the new order email? I think I'm missing something here.
// item name link to product
add_filter( 'woocommerce_order_item_name', 'display_product_title_as_link', 10, 2 );
function display_product_title_as_link( $item_name, $item ) {
$_product = get_product( $item['variation_id'] ? $item['variation_id'] : $item['product_id'] );
$image = wp_get_attachment_image_src( get_post_thumbnail_id( $_product->post->ID ), 'full' );
return ''. $item_name .'
<div style="color:blue;display:inline-block;clear:both;">'.$image[0].'</div>';
}
#LoicTheAztec - it could not work! href as image? Full image as a thumbnail?(big email) Missing permalink to product... Repair function display_product_title_as_link:
function display_product_title_as_link( $item_name, $item ) {
$product = wc_get_product( $item['variation_id'] ? $item['variation_id'] : $item['product_id'] );
$image = wp_get_attachment_image_src( $product->get_image_id(), 'thumbnail' );
$product_name = $product->get_name();
$product_link = get_permalink( $product->get_id() );
return '<img width="70" height="70" src="'.$image[0].'" alt="'. $product_name .'">'. $product_name .' ';
}
First there is some errors in your code like:
The function get_product() is clearly outdated and has been replaced by wc_get_product()
Since Woocommerce 3+ WC_Product properties can be accessed directly, instead use available methods.
Here is the right way to get what you are expecting (in "New Order" admin notification only):
// Your custom function revisited
function display_product_title_as_link( $item_name, $item ) {
$product = wc_get_product( $item['variation_id'] ? $item['variation_id'] : $item['product_id'] );
$image = wp_get_attachment_image_src( $product->get_image_id(), 'full' );
$product_name = $product->get_name();
return ''. $product_name .'
<div style="color:blue;display:inline-block;clear:both;">'.$image[0].'</div>';
}
// The hooked function that will enable your custom product title links for "New Order" notification only
add_action( 'woocommerce_email_order_details', 'custom_email_order_details', 1, 4 );
function custom_email_order_details( $order, $sent_to_admin, $plain_text, $email ){
// Only for "New Order" and admin email notification
if ( 'new_order' != $email->id && ! $sent_to_admin ) return;
// Here we enable the hooked function
add_filter( 'woocommerce_order_item_name', 'display_product_title_as_link', 10, 3 );
}
Code goes in function.php file of your active child theme (active theme or in any plugin file).
Tested and Works in WooCommerce 3+
In WooCommerce, I would like to send a request to an API once the customer has successfully checked out. Its basically a website where the client is selling online courses (Like udemy).
When the customer checks out, I would like to send an API request and enroll the user for that particular course. I have tried several WooCommerce hooks but none worked for me.
This is the code that I'm using:
add_action('woocommerce_checkout_order_processed', 'enroll_student', 10, 1);
function enroll_student($order_id)
{
echo $order_id;
echo "Hooked";
}
I am writing this code for a plugin and to make it easier, I am currently using Cash on Delivery method.
Can anyone point me out where I am going wrong because when I checkout I cant see the message "hooked" that I am printing nor the $order_id?
It takes me to the success page and doesn't show these two things that I am printing.
Update 2 Only For Woocommerce 3+ (added restriction to execute the code only once)
add_action('woocommerce_thankyou', 'enroll_student', 10, 1);
function enroll_student( $order_id ) {
if ( ! $order_id )
return;
// Allow code execution only once
if( ! get_post_meta( $order_id, '_thankyou_action_done', true ) ) {
// Get an instance of the WC_Order object
$order = wc_get_order( $order_id );
// Get the order key
$order_key = $order->get_order_key();
// Get the order number
$order_key = $order->get_order_number();
if($order->is_paid())
$paid = __('yes');
else
$paid = __('no');
// Loop through order items
foreach ( $order->get_items() as $item_id => $item ) {
// Get the product object
$product = $item->get_product();
// Get the product Id
$product_id = $product->get_id();
// Get the product name
$product_id = $item->get_name();
}
// Output some data
echo '<p>Order ID: '. $order_id . ' — Order Status: ' . $order->get_status() . ' — Order is paid: ' . $paid . '</p>';
// Flag the action as done (to avoid repetitions on reload for example)
$order->update_meta_data( '_thankyou_action_done', true );
$order->save();
}
}
Code goes in function.php file of your active child theme (or theme) or also in any plugin file.
Related thread:
Get Order items and WC_Order_Item_Product in WooCommerce 3
How to get WooCommerce order details
The code is tested and works.
Updated (to get the product Id from Orders items as asked in your comment)
May be you could use woocommerce_thankyou hook instead, that will display on order-received page your echoed code, this way:
add_action('woocommerce_thankyou', 'enroll_student', 10, 1);
function enroll_student( $order_id ) {
if ( ! $order_id )
return;
// Getting an instance of the order object
$order = wc_get_order( $order_id );
if($order->is_paid())
$paid = 'yes';
else
$paid = 'no';
// iterating through each order items (getting product ID and the product object)
// (work for simple and variable products)
foreach ( $order->get_items() as $item_id => $item ) {
if( $item['variation_id'] > 0 ){
$product_id = $item['variation_id']; // variable product
} else {
$product_id = $item['product_id']; // simple product
}
// Get the product object
$product = wc_get_product( $product_id );
}
// Ouptput some data
echo '<p>Order ID: '. $order_id . ' — Order Status: ' . $order->get_status() . ' — Order is paid: ' . $paid . '</p>';
}
Code goes in function.php file of your active child theme (or theme) or also in any plugin file.
The code is tested and works.
Then you can use all class WC_Abstract_Order methods on the $order object.
Related:
How to get WooCommerce order details
Get Order items and WC_Order_Item_Product in WooCommerce 3
How to get Customer details from Order in WooCommerce?
Rather than 'woocommerce_thankyou' hook, 'woocommerce_checkout_order_processed' hook is the relevant hook. 'woocommerce_checkout_order_processed' hook will be called only once and you will not need to add meta for each product and make additional calls to keep check for code to run only once. As, 'woocommerce_thankyou' can be called multiple times that is each time thankyou page loads.
Replace add_action('woocommerce_thankyou', 'enroll_student', 10, 1);
with
add_action('woocommerce_checkout_order_processed', 'enroll_student', 10, 1);
and remove meta code and checks. Updated code is
add_action('woocommerce_checkout_order_processed', 'enroll_student', 10, 1);
function enroll_student( $order_id ) {
// Getting an instance of the order object
$order = wc_get_order( $order_id );
if($order->is_paid())
$paid = 'yes';
else
$paid = 'no';
// iterating through each order items (getting product ID and the product object)
// (work for simple and variable products)
foreach ( $order->get_items() as $item_id => $item ) {
if( $item['variation_id'] > 0 ){
$product_id = $item['variation_id']; // variable product
} else {
$product_id = $item['product_id']; // simple product
}
// Get the product object
$product = wc_get_product( $product_id );
}
// Ouptput some data
echo '<p>Order ID: '. $order_id . ' — Order Status: ' . $order->get_status() . ' — Order is paid: ' . $paid . '</p>';
}
you can get the order items of an order by
// Getting an instance of the order object
$order = new WC_Order( $order_id );
$items = $order->get_items();
//Loop through them, you can get all the relevant data:
foreach ( $items as $item ) {
$product_name = $item['name'];
$product_id = $item['product_id'];
$product_variation_id = $item['variation_id'];
}