In WooCommerce 5.6.0 the Woo team add a hook to the email send function. This hook was requested by myself a while back as I needed the ability to generate an order note when a Woo order email was sent. My original proposal was for a hook added in the email classes but Claudio Sanchez suggested instead a single hook in the WooCommerce WC_Email class which works very nicely. With a single line of code I’ve now got the ability to add any action I need when a Woo email has been sent.

woocommerce_email_sent

The new hook is called “woocommerce_email_sent” and is part of the send() function in WC_Email.

/**
	 * Send an email.
	 *
	 * @param string $to Email to.
	 * @param string $subject Email subject.
	 * @param string $message Email message.
	 * @param string $headers Email headers.
	 * @param array  $attachments Email attachments.
	 * @return bool success
	 */
	public function send( $to, $subject, $message, $headers, $attachments ) {
		add_filter( 'wp_mail_from', array( $this, 'get_from_address' ) );
		add_filter( 'wp_mail_from_name', array( $this, 'get_from_name' ) );
		add_filter( 'wp_mail_content_type', array( $this, 'get_content_type' ) );

		$message              = apply_filters( 'woocommerce_mail_content', $this->style_inline( $message ) );
		$mail_callback        = apply_filters( 'woocommerce_mail_callback', 'wp_mail', $this );
		$mail_callback_params = apply_filters( 'woocommerce_mail_callback_params', array( $to, $subject, $message, $headers, $attachments ), $this );
		$return               = $mail_callback( ...$mail_callback_params );

		remove_filter( 'wp_mail_from', array( $this, 'get_from_address' ) );
		remove_filter( 'wp_mail_from_name', array( $this, 'get_from_name' ) );
		remove_filter( 'wp_mail_content_type', array( $this, 'get_content_type' ) );

		/**
		 * Action hook fired when an email is sent.
		 *
		 * @since 5.6.0
		 * @param bool     $return Whether the email was sent successfully.
		 * @param int      $id     Email ID.
		 * @param WC_Email $this   WC_Email instance.
		 */
		do_action( 'woocommerce_email_sent', $return, $this->id, $this );

		return $return;
	}

Source: https://github.com/woocommerce/woocommerce/blob/5128a45936cbd4ad9f3e48770dfbc5ba8ce60c90/includes/emails/class-wc-email.php#L669

The hook gives me 3 arguments:

  • $return: a boolean true / false value whether the email was sucessfully sent
  • $this->id: a string value of the email id, for example “customer_processing_order”
  • $this: an instance of WC_email (object)

Addding an order note when a customer email is sent

For my project the requirement is to add an order note whenever time an order email is sent to the customer. This makes the automatic email sending on order status transition by the WooCommerce plugin transparent to admins who can now easily check which emails were sent when to whom.

The code I wrote looks like this; and is placed in the functions.php file of a custom (child) theme:

/**
* Add order note when WooCommerce order email is sent
* uses hook do_action( 'woocommerce_email_sent', $return, $this->id, $this );
* since 5.6.0
* @param bool   $return Whether the email was sent successfully.
* @param string $id Email ID.
* @param object $email_object  WC_Email instance.
**/
add_action('woocommerce_email_sent', 'ag_woocommerce_email_sent', 10, 3);
function ag_woocommerce_email_sent(  $return, $id, $email_object  ) {

	// only log emails to customers
	if ( substr( $id, 0, 8 ) === "customer" ) {
	
		// let's check that we have an object and that it's an order 
		if( is_object( $email_object->object ) && ($email_object->object instanceof WC_Order ) ) {
			
			// if email was successfully sent add note 
			if( $return ) {
				$order_id = $email_object->object->get_id(); // get order id 
				$title = $email_object->title;  // get email title 
				$recipient = $email_object->recipient; // get email recipient
				$order = new WC_Order( $order_id ); // create new order object 
				
				$note = 'Email Success. "' . $title . '" sent to ' . $recipient; 
				$order->add_order_note( $note );
			}
			
		} // ends class check 
	} // ends customer email check 
	
}

Let’s go through this in detail!

Frst I hook into the action with my own function:

qdd_action('woocommerce_email_sent', 'ag_woocommerce_email_sent', 10, 3);
function ag_woocommerce_email_sent(  $return, $id, $email_object  ) {

Then I check if the order ID starts with “customer”. On the site I work with and in default Woo, all emails to customers start with this string. If you have your own custom email classes this may be different.

// only log emails to customers
if ( substr( $id, 0, 8 ) === "customer" ) {

The email instance contains the order instance in $this->object. So I’m adding a line to check if I indeed have an object and if it’s an order:

// let's check that we have an object and that it's an order 
if( is_object( $email_object->object ) && ($email_object->object instanceof WC_Order ) ) {

Then I check if the email was sent successfully:

// if email was successfully sent add note 
if( $return ) {

As next step I get the order id from the order instance, and construct a new order object.

$order_id = $email_object->object->get_id(); // get order id 
$order = new WC_Order( $order_id ); // create new order object 

I also need the email title and the recipient email address. This is part of the WC_Email instance:

$title = $email_object->title;  // get email title 
$recipient = $email_object->recipient; // get email recipient

And as last step I construct the note and add it to the order:

$note = 'Email Success. "' . $title . '" sent to ' . $recipient; 
$order->add_order_note( $note );

πŸŽ‰ Success!

And that’s it! πŸŽ‰ In WP Admin > Orders > Edit Orders I will now see a note in the right hand bar whenever an email is sent to the customer.

2 responses to “How to use the new WooCommerce hook “woocommerce_email_sent””

  1. David Sosa Avatar
    David Sosa

    Excellent, Thanks for share!

    1. Edith Allison Avatar
      Edith Allison

      Glad it’s of use! πŸ˜€

Leave a Reply

Your email address will not be published. Required fields are marked *