Skip to content
Advertisement

Best practice to add elements to email sent by Woocommerce

I’m pretty new with Woocommerce, and I know a lot of lenguages… except PHP! Furthermore, I don’t understand really well how works WordPress hooks and callbacks, so for the task I want to do I am pretty lost.

The only thing I want to do is add a QR code to the email sent when an order is completed.

What I understood first is that I had to override the file customer-completed-order.php and put it in my theme folder.

Thing is, this file is like that :

<?php
/**
 * Customer completed order email
 *
 * This template can be overridden by copying it to yourtheme/woocommerce/emails/customer-completed-order.php.
 *
 * HOWEVER, on occasion WooCommerce will need to update template files and you
 * (the theme developer) will need to copy the new files to your theme to
 * maintain compatibility. We try to do this as little as possible, but it does
 * happen. When this occurs the version of the template file will be bumped and
 * the readme will list any important changes.
 *
 * @see https://docs.woocommerce.com/document/template-structure/
 * @package WooCommerceTemplatesEmails
 * @version 3.7.0
 */

if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

/*
 * @hooked WC_Emails::email_header() Output the email header
 */
do_action( 'woocommerce_email_header', $email_heading, $email ); ?>
s

<?php /* translators: %s: Customer first name */ ?>
<p><?php printf( esc_html__( 'Hi %s,', 'woocommerce' ), esc_html( $order->get_billing_first_name() ) ); ?></p>
<p><?php esc_html_e( 'We have finished processing your order.', 'woocommerce' ); ?></p>
<?php

/*
 * @hooked WC_Emails::order_details() Shows the order details table.
 * @hooked WC_Structured_Data::generate_order_data() Generates structured data.
 * @hooked WC_Structured_Data::output_structured_data() Outputs structured data.
 * @since 2.5.0
 */
do_action( 'woocommerce_email_order_details', $order, $sent_to_admin, $plain_text, $email );

/*
 * @hooked WC_Emails::order_meta() Shows order meta data.
 */
do_action( 'woocommerce_email_order_meta', $order, $sent_to_admin, $plain_text, $email );

/*
 * @hooked WC_Emails::customer_details() Shows customer details
 * @hooked WC_Emails::email_address() Shows email address
 */
do_action( 'woocommerce_email_customer_details', $order, $sent_to_admin, $plain_text, $email );



/**
 * Show user-defined additional content - this is set in each email's settings.
 */
if ( $additional_content ) {
    echo wp_kses_post( wpautop( wptexturize( $additional_content ) ) );
}

/*
 * @hooked WC_Emails::email_footer() Output the email footer
 */
do_action( 'woocommerce_email_footer', $email );

It doesn’t seem to be the place to add my callback. What I did is add this code to functions.php in my theme, and I would like to know if it is the fine place, and if my code is acceptable too (to keep it simple I didn’t do with QR code for now):

add_action( 'woocommerce_email_before_order_table', 'add_qr_code_to_email', 20, 4 );
  
function add_qr_code_to_email( $order, $sent_to_admin, $plain_text, $email ) {
   if ( $email->id == 'customer_completed_order' ) {
      echo '<h2 class="email-upsell-title">Get 20% off</h2><p class="email-upsell-p">Thank you for making this purchase! Come back and use the code "<strong>Back4More</strong>" to receive a 20% discount on your next purchase! Click here to continue shopping.</p>';
   }
}

Finally, my last question is (it’s not the most important): Why does it work? I thought it was mandatory to “call the callback” with “do_action” in customer-completed-order.php but it seems that my function is still called

Advertisement

Answer

  1. That’s right. You don’t add functions to the template files. You add them at functions.php and then run them from the templates.

  2. Your code seems OK. A common approach is to create different files and them import them into your functions.php. But that’s for easier maintenance and readability. You can look at an example of how this is done in this repo

  3. Why does it work? This is actually the most important question as it’s key to understanding how WP and WC development works.


If you look at that file you’re overriding closely, it pretty much just outputs markup/content and executes actions (do_action) built in with WooCommerce.

I would suggest reading the docs (see links at the end), but to answer your question in simple terms:

Actions allow us to run functions at a specific point in the execution of WordPress Core, plugins, and themes. In other words an Action Hook is how WordPress gives us the ability to extend or modify the core functionality.

These actions can be assigned different callback functions. Those callback functions will be run when the action they’re hooked to is run.

So coming back, each one of those do_action() functions in your template is running an action which just runs all the functions hooked to it (in this case by WooCommerce).

So, It works because you’re hooking your function to the 'woocommerce_email_before_order_table' action.

If you read the comments above each do_action it actually says what’s hooked to them. The 'woocommerce_email_order_details' action’s comment, in your template, says it has WC_Emails::order_details() hooked and that it outputs the order table.

Now, THAT function is getting a template file and rendering it. And the action you hooked into in your code is run in that template. (See line 403 of wp-content/plugins/woocommerce/includes/class-wc-emails.php for that Class and it’s methods).

Hope I was clear enough.

Updated list of resources to read more:

P.s: Actions are 1 of the 2 types of hooks in WP. There’s also filters.

User contributions licensed under: CC BY-SA
2 People found this is helpful
Advertisement