Using PHPMailer to create a centralized email system in Cake PHP

|
Prior to Cake PHP 1.2 there were no built-in email components that were readily available and people had to resort to using their own components with PHPMailer. I do still believe it is better to use a custom PHPMailer component under most circumstances as it is easier to configure, supports more services (ie Gmail), and gives people far greater email authoring options. However currently the number one reason for me to continue using a PHPMailer component is that fact that it allows me to centralize all email communication within an application; although my approach may strike some as mixing the controller/model boundaries too much.

The component is a basic wrapper around PHPMailer, which you should download and unpack into a folder called yourapp/app/vendors/mailer such that yourapp/app/vendors/mailer/class.phpmailer.php exist. Then in your components directory create the file mailer.php with the following content:


<?php
/*
Emails:
sendSampleEmail
(I normally list all emails here for quick reference)
*/
class MailerComponent extends Object {
var $phpMailer;
var $testMode = false;
var $from = 'automailer@example.com';
var $fromName = 'Example.com Automailer';
var $sig = "Regards,

Automailer";
var $parent;

// Create test email for use in testing mode
function createTestEmail($email) {
$email = preg_replace('/(.*?)@(.*)/', "testmail+\$1@gmail.com", $email);
return $email;
}

// Startup functions
function startup(&$controller) {
App::import('vendor', 'Mailer', array('file' => 'mailer/class.phpmailer.php'));
$this->parent =& $controller;
}

// Send an email
function send($to = null, $subject = null, $message = null, $attachments = array(), $riders = array(), $replyTo = null, $replyToName = '', $from = null, $fromName = null) {
// Set up mail
$this->phpMailer = new PHPMailer();
$this->phpMailer->IsSendmail();
$this->phpMailer->From = ($from) ? $from : $this->from;
$this->phpMailer->FromName = ($fromName) ? $fromName : $this->fromName;
$this->phpMailer->Subject = $subject;
$this->phpMailer->MsgHTML($message);

// Set reply to
if ($replyTo) $this->phpMailer->AddReplyTo($replyTo, $replyToName);

// Test mode switching, can also do domain based switching
if (true) {
$this->testMode = true;
}

// Add address(s)
if (!is_array($to)) $to = array($to);
foreach ($to as $address) {
if ($this->testMode) $address = $this->createTestEmail($address);
$this->phpMailer->AddAddress($address);
}

// Add rider(s)
if (!$this->testMode) $this->phpMailer->AddBCC('backup@example.com');
if (!empty($riders)) {
foreach ($riders as $r) {
if ($this->testMode) $r = 'tester@example.com';
$this->phpMailer->AddBCC($r);
}
}

// Add attachments
foreach ($attachments as $a) {
$this->phpMailer->AddAttachment($a);
}

// Send email
$success = $this->phpMailer->Send();
$this->phpMailer->ClearAddresses();
$this->phpMailer->ClearAttachments();
return $success;
}

// Send sample email with data from a form
function sendSampleEmail($data) {
$subject = "Sample Email - Contact from {$data['name']} ({$data['ip']})";
$contents = array();
foreach ($data as $field => $value) $contents[] = ucwords($field) . ": {$value}";
$contents = join("\n", $contents);
$message = nl2br("Dear Admin,

The following contact request was received.

---- Begin contact contents ----

{$contents}

---- End contact contents ----

{$this->sig}");
$this->send('admin@example.com', $subject, $message);
}
}
?>


The component has support for sending all emails to a testing account and centers around the send function. The send function parameters are pretty self explanatory with the only weird parameter being $riders, which is my unconventional way of saying bccs. Each call of the send function creates a new PHPMailer object, which may seem wasteful but in my experience prevents a lot of strange issues such as person B receiving email content intended for person A that was sent previously.

The sample email function in the component sends a very simple contact form to the administrator of example.com. The $data variable is provided by the controller and is then used to compose a unique email. The message that is composed is very plain HTML that is simply a string with all new lines converted to HTML line breaks. For those needing to send complex HTML emails I normally have the message data be retrieve by calling something like $this->parent->requestAction("/mycontroller/emailFor/12"); which means the email is rendered by just another function in a controller that can be styled and tested easily.

As the $this->parent refers to the calling controller one can use that to load models, perform finds, access submitted variables, etc. The only thing to keep in mind is the level of dependency between the component and the calling controller. Although I do not find this to be a problem as the component is more of a function-based container that makes managing email content easier. Under extremely simple circumstances one would have the email functions be stored in the relevant controller instead of in a separate component.

To use the component just include it as a component in your controller with:


var $components = array('Mailer');


And allow sending with a function like:


function contact() {
if ($this->data) {
$this->Mailer->sendSampleEmail($this->data['Contact']);
$this->Session->setFlash('Your message has been received. We will get back to you shortly.');
$this->redirect('/contact');
}
}


While the component is set up to use the default mail sender it is also possible to set it up using a hosted provider like Gmail. Just see my post on using Gmail with PHPMailer to view the configuration settings and place them in the "Set up mail" section of the send function.

0 comments:

Post a Comment