Modernizing Extras: Handling Emails

Sending mail in the code of your Extra.

By Bob Ray  |  July 12, 2022  |  4 min read
Modernizing Extras: Handling Emails

This is a series of articles to help MODX Extras Authors to update their code for MODX Revolution 3.x while making it easy to maintain a version that works with Revo 2.x, too. If your custom Extras send email, you’ll need to make some updates for 3.0 in order for the mail to reach its destination. In this article, we’ll look at some of the necessary changes, starting with some Revo 3 specific mail code.

Separate Package for Revo 3

Here’s the code for sending mail in Revo 3:

use MODX\Revolution\Mail\modPHPMailer;

$sender = '[email protected]';
$recipient = '[email protected]';
if (!$modx->services->has('mail')) {
    $modx->services->add('mail', new modPHPMailer($modx));
}
$mail = $modx->services->get('mail');

/* Set the mail fields and options */
$mail->set(modMail::MAIL_BODY, 'Testing Mail Service');
$mail->set(modMail::MAIL_FROM, $sender);
$mail->set(modMail::MAIL_FROM_NAME, 'Bob');
$mail->set(modMail::MAIL_SENDER, $sender);
$mail->set(modMail::MAIL_SUBJECT, 'TESTING');
$mail->address('to', $recipient);
$mail->address('reply-to', $sender);
$mail->setHTML(true);

/* Send the email */
if (!$sent = $mail->send()) {
    $err = 'Error: ' . $mail->mailer->ErrorInfo;
    $this->modx->log(xPDO::LOG_LEVEL_ERROR, $err);
    return 'Failure';
} else {
    $mail->reset();
    return 'Mail sent';
}

Once you’ve called $modx->services->add(), the class is stored in the MODX services container. It can be retrieved anywhere the $modx object is available. If you need it somewhere else, you could get it again, but there are easier ways to handle that.

Depending on your use case, you may need the $mail service in various places. If you need the service in more than one method in a class, for instance, you can use $this->mail = $modx->services->get('mail');. That way it will be available anywhere in the class. If you need it outside the class, or in other classes, you can do this:

if (!$modx->services->has('mail')) {
    $modx->services->add('mail', new modPHPMailer($modx));
}
$this->modx->mail = $modx->services->get('mail');

That will store the modPhpMailer instance in the $modx->mail class variable. Once that’s done, you can use the line below and follow it with the mail code at the beginning of this section anywhere that instance of MODX is available, even when it’s passed to another class:

$mail = $modx->mail;

One Package for Both Revo 2 and Revo 3

Because the modPhpMailer class itself is essentially the same in Revo 2 and Revo 3, we don’t need to mess with abstract classes or dynamic parents here. We also don’t need a prefix, unless you need it in other parts of your code. The only caveat is that we can’t have a use statement for the mail class. We need to use the fully qualified name of the service we want. We just need to do this:

$isMODX3 = $modx->getVersionData()['version'] >= 3;

if ($isMODX3) {
    if (!$modx->services->has('mail')) {
        $modx->services->add('mail', new MODX\Revolution\Mail\modPhpMailer($modx));
    }
    $mail = $modx->services->get('mail');
} else {
    $mail = $modx->getService('mail', 'mail.modPhpMailer');
}

(If this is inside a class, you’d use $this->mail in place of $mail.) Once that’s done, you can use code like the following for both Revo 2 and Revo 3:

$sender = '[email protected]';
$recipient = '[email protected]';

/* Set the mail fields and options */
$mail->set(modMail::MAIL_BODY, 'Testing Mail Service');
$mail->set(modMail::MAIL_FROM, $sender);
$mail->set(modMail::MAIL_FROM_NAME, 'Bob');
$mail->set(modMail::MAIL_SENDER, $sender);
$mail->set(modMail::MAIL_SUBJECT, 'TESTING');
$mail->address('to', $recipient);
$mail->address('reply-to', $sender);
$mail->setHTML(true);

/* Send the email */
if (!$sent = $mail->send()) {
    $err = 'Error: ' . $mail->mailer->ErrorInfo;
    $this->modx->log(xPDO::LOG_LEVEL_ERROR, $err);
    return 'Failure';
} else {
    $mail->reset();
    return 'Mail sent';
}

It’s important to call $mail->reset() after the mail is sent. That empties all the mail field values in the class. Otherwise, another call to the mail service that doesn’t set them all will use the old values for the fields that aren’t set when it sends the mail.


Bob Ray is the author of the MODX: The Official Guide and dozens of MODX Extras including QuickEmail, NewsPublisher, SiteCheck, GoRevo, Personalize, EZfaq, MyComponent and many more. His website is Bob’s Guides. It not only includes a plethora of MODX tutorials but there are some really great bread recipes there, as well.