Well, this isn’t a full solution per se, but I would start by isolating whether the problem is the SQL queries or the wp_mail()
call. If you comment out the wp_mail()
line, you can get an idea of how long the function takes to run the queries and build the messages without trying to send them.
If you find that suddenly the function is lightning fast, then it’s a fair bet that the mail connections are what’s slowing you down, but unfortunately that opens another can of worms since email configurations can vary wildly from web host to web host.
If on the other hand the function is just as slow, then the queries are the likely culprit, and then it would be very helpful to see the actual SQL that WordPress is generating in order to try to identify the performance leak.
EDIT:
Sounds like it is the queries doing many many JOINs after all. In that case, the goal is to avoid that. We can take advantage of the fact that we’re looking for different cases of the same wp_usermeta.meta_key
values to build a query that doesn’t need all the JOINs:
function messages_notify() {
global $post;
// if post already published, abort
if($post->post_status == 'publish') {
return;
}
// get districts ticked, else abort
$post_districts = get_the_terms($post->ID, 'message_district');
if(empty($post_districts)) return;
// Build a list of districts to get users from.
$districts = array(
'Northeast' => 'whprms_district_ne',
'North Central' => 'whprms_district_nc',
'Southeast' => 'whprms_district_se',
'Southwest' => 'whprms_district_sw',
'West' => 'whprms_district_w',
//'Another' => 'whprms_district_XX',
);
$searchDistricts = array();
$districtNames = array_keys($districts);
foreach($post_districts as $d){
if(in_array($d->name, $districtNames){
$searchDistricts[] = $districts[$d->name];
}
}
$districts = implode(' OR ', array_map(function($s){ return "wp_usermeta.meta_key = '{$s}'"; }, $searchDistricts)); // Squash it down into an sql-compatible WHERE subclause.
$sql = "SELECT DISTINCT wp_users.*
FROM wp_users
INNER JOIN wp_usermeta ON (wp_users.ID = wp_usermeta.user_id)
WHERE
-- This part isolates to keys in the list of districts
({$districts})
-- Where the value of the district is 1.
AND CAST(wp_usermeta.meta_value AS CHAR) = '1'
ORDER BY user_login ASC";
$district_members = $wpdb->WHATEVER_THE_CUSTOM_QUERY_METHOD_IS_CALLED($sql);
$attachment_args = array('post_type' => 'attachment', 'orderby' => 'date', 'order' => 'ASC', 'numberposts' => -1, 'post_status' => NULL, 'post_parent' =>$post->ID);
$attachments = get_posts($attachment_args);
if ($attachments) {
$att_array = array();
foreach ($attachments as $attachment) {
$att_array[] = get_the_title($attachment->ID);
}
}
// message only users with desired role(s)
$bcc = array();
foreach($district_members as $district_member) {
// Don't use user_can()! It's a DB hit!
//if(user_can($district_member->ID, 'board_members') || user_can($district_member->ID, 'members')) { //@TODO: Look at $district_member->caps
if(in_array('board_members', $district_member->roles) || in_array('members', $district_member->roles)) {
$bcc[] = $district_member->user_email;
}
}
$subject="WHPRMS Message: ".$post->post_title;
$message="New message posted by ";
if(user_can($post->post_author, 'administrator')) {
$message .= get_the_author_meta('user_nicename', $post->post_author)."\n\r";
}
else {
$message .= get_the_author_meta('first_name', $post->post_author).' '.get_the_author_meta('last_name', $post->post_author)."\n\r";
}
$message .= 'Excerpt: '.stripslashes(substr($post->post_content, 0, 240)).'...'."\n\r";
$message .= 'Link: '.get_permalink($post->ID)."\n\r";
$message .= 'District(s): '.implode(', ', $the_districts)."\n\r";
if (!empty($att_array)) {
$message .= 'Attachment(s): '.implode(', ', $att_array)."\n\r";
}
$message .= 'You are receiving this message because you belong to one of the districts mentioned above.';
$chunked_bcc = array_chunk($bcc, 25);
foreach($chunked_bcc as $bcc_chunk){
$headers = array();
$headers['Bcc'] = 'BCC: '.implode(', ', $bcc_chunk);
wp_mail('[email protected]', $subject, $message, $headers);
}
}
A couple things this doesn’t take into account, since I don’t have this particular WP instance in front of me to test with:
- WHATEVER_THE_CUSTOM_QUERY_METHOD_IS_CALLED. Replace this with whatever the right method is.
- I also don’t know what format that will return results in. If you can get WP_user objects out of it, that’d be ideal, because the next section depends on $district_users looking the same as what
get_users()
provided. - Also pay attention to the change in
foreach($district_members as $district_member)
. Theuser_can()
method is 2 unnecessary DB hits for EVERY user you’re looping over. You have the user’s capabilities in the results returned from the query already– use that instead.