Thursday, March 26, 2009

Emailing from CakePHP 1.2.x

I've recently started working on a project in CakePHP and I wanted to share something that came out of it ...

Sending nice emails from any website is always fun, as we all know PHP has some very basic mail functionality that everyone has extended in their own way. I was trying to add email functionality to a site that I am developing and when I googled for anything to help I found this post from Alex McFayden that had a way to integrate PHPMailer into CakePHP as a component for any controller. Unfortunately it was written in 2006 for a previous version, obviously some changes were needed, so here are the new integration methods.

Get PHPMailer

  1. Get PHPMailer http://phpmailer.sourceforge.net/
  2. Unpack it into app/vendors/phpmailer/ , so you'll have /vendors/phpmailer/class.phpmailer.php etc.etc.

Create views and layouts

  1. Create two views, default_html.thtml and default_text.thtml and place them in app/views/your_controller/email/
  2. Create a layout for the HTML part of the email, call it app/views/layouts/email.ctp

Create component

  1. Create new component email. Paste the following code into app/controllers/components/email.php

<?php
/**
* This is a component to send email from CakePHP using PHPMailer
* @link http://bakery.cakephp.org/articles/view/94
* @see http://bakery.cakephp.org/articles/view/94
*/

class EmailComponent
{
/**
* Send email using SMTP Auth by default.
*/
var $from = 'some_email@goes.here.com';
var $fromName = "Displayed Name";
var $sitePrefix = '[MySite]';
var $useSMTPAuth = false;
var $smtpUserName = '';
var $smtpPassword = '';
var $smtpHostNames = "localhost:25";
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 = 'email/default';
var $attachments = null;

var $controller;

function startup( & $controller)
{
$this->controller = & $controller;
}

/**
* Helper function to generate the appropriate template location
*
* @return string CakePHP location of the template file
* @param object $template_type
*/
function templateLocation($template_type)
{
return ('..'.DS.strtolower($this->controller->name).DS.$this->template.$template_type);
}

/**
* Renders the content for either html or text of the email
*
* @return string Rendered content from the associated template
* @param object $type_suffix
*/
function bodyContent($type_suffix)
{
$temp_layout = $this->controller->layout; // store the current controller layout

if ($type_suffix == 'html')
$this->controller->layout = '..'.DS.'email';
else
$this->controller->layout = '';

$mail = $this->controller->render($this->templateLocation('_'.strtolower($type_suffix)));
// render() automatically adds to the controller->output, we'll remove it
$this->controller->output = str_replace($mail, '', $this->controller->output);

$this->controller->layout = $temp_layout; // restore the controller layout
return $mail;
}

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;
}
}

function send()
{
App::import('Vendor', 'PHPMailer', array ('file'=>'phpmailer'.DS.'class.phpmailer.php'));

$mail = new PHPMailer();

$mail->IsSMTP();
$mail->SMTPAuth = $this->useSMTPAuth;
$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 = 80; // 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->IsHTML(true); // set email format to HTML

$mail->Subject = $this->sitePrefix.' '.$this->subject;
$mail->Body = $this->bodyContent('html');
$mail->AltBody = $this->bodyContent('text');

$result = $mail->Send();

if ($result == false)
$result = $mail->ErrorInfo;

return $result;
}
}
?>


Then, to use this component you can do the following in your controller ...


var $components = array('Email');

function send_verification($id)
{
$this->Email->template = 'email/default';

$this->set('data', $this->data);
$toUser = $this->User->find(array('User.id'=>$id));
$this->Email->to = $toUser['User']['email'];
$this->Email->subject = 'Your new account';

//$this->Email->attach($fully_qualified_filename, optionally $new_name_when_attached);
// You can attach as many files as you like.

$result = $this->Email->send();
}


I've tested it and it works even when used from another function in the same controller being called as a JSON or XML action extension.

Saturday, March 07, 2009

Restarting my thoughts ...

Ok, everything old is gone ... I'll be starting up a new topic schedule in this space over the next two weeks as I reorganize my goals and ambitions ... for those less technically oriented it may be a bit on the geeky side and for those who enjoy a bit of geek in their lives I hope to offer a funny/informative take on things ...