Sending Email With PHPMailer

This example will show you how to send HTML mail from you Cake application with PHPMailer. You will create:

  1. cakePHP component
  2. a vendor package
  3. view for plain text email body
  4. view for HTML email body
  5. a function in your controller to send mail.

TODO: Show how to send attachments and images inside HTML.

Steps

Get PHPMailer

  1. Unpack it into app/vendors/phpmailer/ , so you’ll have /vendors/phpmailer/class.phpmailer.php etc.etc.

Create views

  1. Create two views , email_body_html.thtml and email_body_plain.thtml and 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

  1. 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);

 
tutorials/sending_email_with_phpmailer.txt · Last modified: 2006/10/12 14:12 by fluxx