Sending HTML email containing custom fields to site users from WordPress

The Basics

You can send an email from anywhere within WordPress using wp_mail():

wp_mail( $to, $subject, $message, $headers, $attachments )

$to can be a string or an array for multiple recipients. Any RFC 2822 compliant address will work, so you can include a real name as well as an email address.

$subject & $message are strings and $message can be a string of HTML.

Optionally, $headers can be a string or an array of additional email headers and $attachments can be an array of files to attach.

HTML Email

If you are using HTML for the content, then before sending the email you can add a simple function to a filter that will handle setting the mime type correctly for you:

function wpse306737_set_html_content_type($content_type){
    return 'text/html';
}

add_filter( 'wp_mail_content_type', 'wpse306737_set_html_content_type' );

After sending your email it’s good practice to remove this filter again so that you don’t affect any email that might be sent later in the WP code run:

remove_filter( 'wp_mail_content_type', 'wpse306737_set_html_content_type' );

The From: Address

By default emails come from wordpress@sitename. You can use filters to change this:

add_filter( 'wp_mail_from', 'wpse306737_mail_from' );
add_filter( 'wp_mail_from_name', 'wpse306737_mail_from_name' );

function wpse306737_mail_from() {
    return '[email protected]';
}

function wpse306737_mail_from_name() {
    return 'John Smith';
}

You can also set headers if you prefer,

$headers[] = 'From: John Smith <[email protected]>';

but WP filters seem more in keeping with the WP Way.

Other Headers

$headers[] = 'Cc: The Shop <[email protected]>';
$headers[] = 'Bcc: Jane Jones <[email protected]>';

Building the Message

The message is just a string, so concatenate plain text, HTML and any data from WP that you want. If we are using post meta fields and are in the loop, then:

$message="";
$custom_fields = get_post_custom();
$message .= 'You have ' . $custom_fields['X'] . ' credits remaining to fulfill ' . $custom_fields['Y'] . '.';
$message .= 'Your deadline is ' . $custom_fields['Z'] . '.';

A note on retrieving meta data

get_post_custom() returns all custom fields for the current post when you’re within the loop. You can call it with a post ID to get the meta data for particular post: get_post_custom(3) for example.

get_post_custom() will return an array of all post meta, some of it possibly serialised, some of it possibly in arrays and so you may find it better to use a number of calls to get_post_meta() which will let you get your data by metakey and specify whether it may be single or multiple values: get_post_meta( 3, 'total_points', true ) will return a single value for the meta field with key total_points attached to the post with ID 3.

You don’t state how the data you want to use is stored, but there are equivalent functions for retrieving user meta, term meta and comment meta. get_user_meta(), get_term_meta() & get_comment_meta() all work in the same way as get_post_meta().

In fact if you look at the source code, WP will happily return any meta as long as there is a database table for it, so you could even use built-in functions and take advantage of the WordPress meta cache for your own database tables if your tables have suitable names. The get_{type}_meta() functions I’ve referred to all call upon get_metadata(). Calling get_metadata( 'thing', 3 ) will check for a WP db table called thingmeta and will use it to retrieve meta data. get_metadata() also has a filter get_{$meta_type}_metadata to short-circuit this database lookup allowing you to substitute any data source that you like.

Sending the Mail

Putting all this together, you get:

add_filter( 'wp_mail_from', 'wpse306737_mail_from' );
add_filter( 'wp_mail_from_name', 'wpse306737_mail_from_name' );

function wpse306737_mail_from() {
    return '[email protected]';
}

function wpse306737_mail_from_name() {
    return 'John Smith';
}

$headers = array(); // let's be safe
$headers[] = 'Cc: The Shop <[email protected]>';
$headers[] = 'Bcc: Jane Jones <[email protected]>';

$message="";
$custom_fields = get_post_custom();
$message .= '<p>You have ' . $custom_fields['X'] . ' credits remaining to fulfill ' . $custom_fields['Y'] . '.</p>';
$message .= '<p><strong>Your deadline is ' . $custom_fields['Z'] . '.</strong></p>';

$to = 'Site User <[email protected]>';
$subject="Meet your deadline!";

function wpse306737_set_html_content_type($content_type){
    return 'text/html';
}

add_filter( 'wp_mail_content_type', 'set_html_content_type' );

wp_mail( $to, $subject, $message, $headers );

remove_filter( 'wp_mail_content_type', 'set_html_content_type' );

Sending Loads of Email

You might want to preserve your server’s reputation and improve deliverability by sending the email out through a valid SMTP account or a transactional email service, so that you can take advantage of SPF, DKIM and DMARC.