Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

A custom email

The standard emails are not always enough. The lesson on statuses introduced the Ready status — the customer hears nothing about it. Your own email is a class extending WC_Email: it gets a row in the settings, a subject, a template and the styling shared by all shop emails.

The email class

Create the file includes/class-acme-email-ready.php:

// wp-content/plugins/acme-shop/includes/class-acme-email-ready.php
class Acme_Email_Ready extends WC_Email
{
    public function __construct()
    {
        $this->id = 'acme_ready';
        $this->customer_email = true;
        $this->title = 'Order ready';
        $this->description = 'Sent to the customer when the order has been made.';
        $this->template_html = 'emails/acme-ready.php';
        $this->template_base = dirname(__DIR__) . '/templates/';
        $this->placeholders = ['{order_number}' => ''];
 
        parent::__construct();
    }
 
    public function get_default_subject(): string
    {
        return 'Order #{order_number} is ready';
    }
 
    public function get_default_heading(): string
    {
        return 'Your order is ready';
    }
}

The properties set the ID, the name in the admin panel and the template path. customer_email means the email goes to the customer. The {order_number} placeholder in the subject is replaced with the order number.

Sending

Add two methods to the class:

// wp-content/plugins/acme-shop/includes/class-acme-email-ready.php
public function trigger(int $orderId): void
{
    $this->object = wc_get_order($orderId);
 
    if (!$this->object || !$this->is_enabled()) {
        return;
    }
 
    $this->recipient = $this->object->get_billing_email();
    $this->placeholders['{order_number}'] = $this->object->get_order_number();
 
    $this->send(
        $this->get_recipient(),
        $this->get_subject(),
        $this->get_content(),
        $this->get_headers(),
        $this->get_attachments()
    );
}
 
public function get_content_html(): string
{
    return wc_get_template_html(
        $this->template_html,
        ['order' => $this->object, 'email_heading' => $this->get_heading(), 'email' => $this],
        '',
        $this->template_base
    );
}

trigger() finds the order, sets the recipient and sends the email. get_content_html() builds the email body from the template and passes variables into it.

The template

Create the file templates/emails/acme-ready.php:

<!-- wp-content/plugins/acme-shop/templates/emails/acme-ready.php -->
<?php do_action('woocommerce_email_header', $email_heading, $email); ?>
 
<p>Hello, <?php echo esc_html($order->get_billing_first_name()); ?>!</p>
<p>Order #<?php echo esc_html($order->get_order_number()); ?> has been made and will be handed to delivery tomorrow.</p>
 
<?php do_action('woocommerce_email_footer', $email); ?>

The header and footer hooks print the shop's common styling: the logo, colors, the signature. The real file starts with <?php.

Registration

// wp-content/plugins/acme-shop/includes/emails.php
add_filter('woocommerce_email_classes', function (array $emails): array {
    require_once __DIR__ . '/class-acme-email-ready.php';
 
    $emails['Acme_Email_Ready'] = new Acme_Email_Ready();
 
    return $emails;
});

The class file is included inside the filter: the WC_Email class is loaded only by that moment. Open WooCommerce → Settings → Emails — an Order ready row has appeared in the list, where the email can be switched off or its subject changed.

Triggering on a status

// wp-content/plugins/acme-shop/includes/emails.php
add_action('woocommerce_order_status_ready', function (int $orderId) {
    $emails = WC()->mailer()->get_emails();
 
    if (isset($emails['Acme_Email_Ready'])) {
        $emails['Acme_Email_Ready']->trigger($orderId);
    }
});

WC()->mailer() loads all shop emails. Move an order to the Ready status in the admin panel: the customer receives an email with the subject Order #1024 is ready.

What an email consists of

PartWhere
The classincludes/class-acme-email-ready.php
The templatetemplates/emails/acme-ready.php
RegistrationThe woocommerce_email_classes filter
TriggeringAn order status hook and the trigger() method

Common mistake

The class file is included in the plugin's main file next to the other require lines. At that moment the WC_Email class does not exist yet, and the site crashes with a class not found error. Include the email class only inside the woocommerce_email_classes filter.

Thanks for reading!

Donate to the site — we work hard for you.