Sending Email With PHPMailer
This example will show you how to send HTML mail from you Cake application with PHPMailer. You will create:
- cakePHP component
- a vendor package
- view for plain text email body
- view for HTML email body
- a function in your controller to send mail.
TODO: Show how to send attachments and images inside HTML.
Steps
Get PHPMailer
- Unpack it into
app/vendors/phpmailer/, so you’ll have/vendors/phpmailer/class.phpmailer.phpetc.etc.
Create views
- Create two views ,
email_body_html.thtmlandemail_body_plain.thtmland place them in app/views/your_controller/
Create component
Create new component email: paste the following code into app/controllers/components/email.php
/** * This is a component to send email from CakePHP using PHPMailer * @link http://wiki.cakephp.org/tutorials:sending_email_with_phpmailer * @see http://wiki.cakephp.org/tutorials:sending_email */ class EmailComponent { /** * Send email using SMTP Auth by default. */ var $from = 'phpmailer@cakephp'; var $fromName = "Cake PHP-Mailer"; var $smtpUserName = 'username'; // SMTP username var $smtpPassword = 'password'; // SMTP password var $smtpHostNames= "smtp1.example.com;smtp2.example.com"; // specify main and backup server var $text_body = null; var $html_body = null; var $to = null; var $toName = null; var $subject = null; var $cc = null; var $bcc = null; var $controller; function startup( &$controller ) { $this->controller = &$controller; } function bodyText() { /** This is the body in plain text for non-HTML mail clients */ ob_start(); $this->controller->render('email_body_plain'); $mail = ob_get_clean(); return $mail; } function bodyHTML() { /** This is HTML body text for HTML-enabled mail clients */ ob_start(); $this->controller->render('email_body_html'); $mail = ob_get_clean(); return $mail; } function send() { vendor('phpmailer'.DS.'class.phpmailer'); $mail = new PHPMailer(); $mail->IsSMTP(); // set mailer to use SMTP $mail->SMTPAuth = true; // turn on SMTP authentication $mail->Host = $this->smtpHostNames; $mail->Username = $this->smtpUserName; $mail->Password = $this->smtpPassword; $mail->From = $this->from; $mail->FromName = $this->fromName; $mail->AddAddress($this->to, $this->toName ); $mail->AddReplyTo($this->from, $this->fromName ); $mail->CharSet = 'UTF-8'; $mail->WordWrap = 50; // set word wrap to 50 characters //$mail->AddAttachment("/var/tmp/file.tar.gz"); // add attachments //$mail->AddAttachment("/tmp/image.jpg", "new.jpg"); // optional name $mail->IsHTML(true); // set email format to HTML $mail->Subject = $this->subject; $mail->Body = $this->bodyHTML(); $mail->AltBody = $this->bodyText(); $success = $mail->Send(); return $success; } }
Use the component from the controller
- In your controller:
var $components = array('Email'); // use component email ... function send() { $this->set('contact', $this->data['contact'] ); $this->Email->to = 'me@my.com'; //$this->Email->cc = 'someone@copied.com'; //$this->Email->bcc = 'someone@blindcopied.com'; $this->Email->subject = 'from Cake Mailer '; $result = $this->Email->send(); //the rest of the controller method... } }
That’s all.
Fixing layout hassles
Olle Jonsson here. I love this. I had a problem with the Email component’s action’s views were wrapped by my default.thtml layout file. To fix that, I did these changes to the Email component’s actions:
function bodyText() { /** This is the body in plain text for non-HTML mail clients */ ob_start(); $temp_layout = $this->controller->layout; $this->controller->layout = ""; // Turn off the layout wrapping $this->controller->render('email_body_plain'); $mail = ob_get_clean(); $this->controller->layout = $temp_layout; // Turn on layout wrapping again return $mail; } function bodyHTML() { /** This is HTML body text for HTML-enabled mail clients */ ob_start(); $temp_layout = $this->controller->layout; $this->controller->layout = ""; $this->controller->render('email_body_html'); $mail = ob_get_clean(); $this->controller->layout = $temp_layout; return $mail; }
After changing that, my email is sent predictably.
Using more than one template
Reading this I didn’t see the point in just having one template for all of my emails. So I changed the code... A TINY bit
1. Create two templates with the same name one for plain text and one for html and then prefix them with _text, _html e.g. confirm_text.thtml
confirm_html.thtml
2. Add a new variable in the EmailCompnent at the end of all of the variable declarations so it looks like.
class EmailComponent { /** * Send email using SMTP Auth by default. */ var $from = 'no-reply@somewhere.com'; var $fromName = "yourwebsite.com"; var $smtpUserName = 'username'; // SMTP username var $smtpPassword = 'password'; // SMTP password var $smtpHostNames= "localhost"; // specify main and backup server var $text_body = null; var $html_body = null; var $to = null; var $toName = null; var $subject = null; var $cc = null; var $bcc = null; var $template = null; // New line we've just added
3. Alter the bodyText and bodyHTML functions so that it looks like this
function bodyText() { /** This is the body in plain text for non-HTML mail clients */ ob_start(); $temp_layout = $this->controller->layout; $this->controller->layout = ''; // Turn off the layout wrapping $this->controller->render($this->template . '_text'); // New line to allow text template $mail = ob_get_clean(); $this->controller->layout = $temp_layout; // Turn on layout wrapping again return $mail; } function bodyHTML() { /** This is HTML body text for HTML-enabled mail clients */ ob_start(); $temp_layout = $this->controller->layout; $this->controller->layout = 'email'; // Contradicting Olle Jonsson's fix I've added a HTML wrapper for my html email in /app/views/layouts $this->controller->render($this->template . '_html'); // New line to allow html template $mail = ob_get_clean(); $this->controller->layout = $temp_layout; // Turn on layout wrapping again return $mail; }
Now when you go to send your email just add “$this→Email→template = ‘email/confirm’;” I generally tend to put my email templates in an email subfolder to my controller views.
So now it should look like
$this->Email->template = 'email/confirm'; $this->set('data', $data); $this->Email->to = 'someone@somewhere.com'; $this->Email->subject = 'your new account'; $result = $this->Email->send();
Hope this helps!!
Gavin Williams
Ability for multiple attachments
Okay, decided to add the ability to add attachments to this much loved CakePHPmailer.
1. First add this new function to your component.
function attach($filename, $asfile = '') { if (empty($this->attachments)) { $this->attachments = array(); $this->attachments[0]['filename'] = $filename; $this->attachments[0]['asfile'] = $asfile; } else { $count = count($this->attachments); $this->attachments[$count+1]['filename'] = $filename; $this->attachments[$count+1]['asfile'] = $asfile; } }
2. Add the following variable to your component.
var $attachments = null;
3. Modify the send function to the following.
function send() { vendor('phpmailer'.DS.'class.phpmailer'); $mail = new PHPMailer(); $mail->IsSMTP(); // set mailer to use SMTP $mail->SMTPAuth = true; // turn on SMTP authentication $mail->Host = $this->smtpHostNames; $mail->Username = $this->smtpUserName; $mail->Password = $this->smtpPassword; $mail->From = $this->from; $mail->FromName = $this->fromName; $mail->AddAddress($this->to, $this->toName ); $mail->AddReplyTo($this->from, $this->fromName ); $mail->CharSet = 'UTF-8'; $mail->WordWrap = 50; // set word wrap to 50 characters if (!empty($this->attachments)) { foreach ($this->attachments as $attachment) { if (empty($attachment['asfile'])) { $mail->AddAttachment($attachment['filename']); } else { $mail->AddAttachment($attachment['filename'], $attachment['asfile']); } } } //$mail->AddAttachment("/var/tmp/file.tar.gz"); // add attachments //$mail->AddAttachment("/tmp/image.jpg", "new.jpg"); // optional name $mail->IsHTML(true); // set email format to HTML $mail->Subject = $this->subject; $mail->Body = $this->bodyHTML(); $mail->AltBody = $this->bodyText(); $success = $mail->Send(); return $success; }
4. To use, now all you do is call $this→Email→attach($fully_qualified_filename, optionally $new_name_when_attached);