Caution: You are browsing the legacy 1.x part of this website.
This version of symfony is not maintained anymore. If some of your projects still use this version, consider upgrading.
This work is licensed under the Creative Commons Attribution-Noncommercial-No Derivative Works 3.0 Unported License license.

Master Symfony2 fundamentals

Be trained by SensioLabs experts (2 to 6 day sessions -- French or English).
trainings.sensiolabs.com

Discover the SensioLabs Support

Access to the SensioLabs Competency Center for an exclusive and tailor-made support on Symfony
sensiolabs.com

How to send an email

Overview

Sending mails from a web application is a very common tasks. Symfony uses its architecture to automate it in a familiar way (MVC separation) and provides a specific class to deal with the particularities of the emails (multiple mime types, enclosed media, attachments).

Introduction

Symfony offers two ways to send emails from your web application:

  • Via the sfMail class, which is a proxy class that offers an interface with PHPMailer. This solution is simple and fast, but doesn't provide MVC separation and is hardly compatible with i18n. Complex emails are also harder to compose with the sfMail class alone.

  • Via a specific action and template. This solution is very versatile and deals with emails just as regular pages, with the addition of the specificities of this media. It is a little longer to put in place, but much more powerful than the first one.

The implementation of both solution will be illustrated through the same example: the sending of a forgotten password requested by a user.

Direct use of sfMail

The sfMail class will look familiar to those who know the PHPMailer class. It is simply a proxy class to PHPMailer, taking advantage of the symfony syntax. The PHPMailer class is included in the symfony package, so no additional installation (nor require) is required.

To send an email containing a password to a customer, an action has to do like the following:

public function executePasswordRequest()
{
  // determine customer from the request 'id' parameter
  $customer = CustomerPeer::retrieveByPk($this->getRequestParameter('id'));
 
  // class initialization
  $mail = new sfMail();
  $mail->initialize();
  $mail->setMailer('sendmail');
  $mail->setCharset('utf-8');
 
  // definition of the required parameters
  $mail->setSender('webmaster@my-company.com', 'My Company webmaster');
  $mail->setFrom('info@my-company.com', 'My Company');
  $mail->addReplyTo('webmaster_copy@my-company.com');
 
  $mail->addAddress($customer->getEmail());
 
  $mail->setSubject('Your password request');
  $mail->setBody('
  Dear customer,
 
  You are so absentminded. Next time, try to remember your password:
  '.$customer->getPassword().'
 
  Regards,
  The My Company webmaster');
 
  // send the email
  $mail->send();
}

Use of an alternate action

In many cases, as the email sending process is just a detour in the logic of an action that does something else, it is often delegated to another action. Here is how it goes:

public function executePasswordRequest()
{
  // send the email
  $raw_email = $this->sendEmail('mail', 'sendPassword');  
 
  // log the email
  $this->logMessage($raw_email, 'debug');
}

The email sending is delegated to a sendPassword action of a mail module. The sendEmail() method of the sfAction class is a special kind of forward() that executes another action but comes back afterward (it doesn't stop the execution of the current action). In addition, it returns a raw email that can be written into a log file (you will find more information about the way to log information in Chapter 16).

The mail/sendPassword deals with the sfMail object, but it doesn't need to define the mailer (it is taken from a configuration file) nor to initialize it:

public function executeSendPassword()
{
  // determine customer from the request 'id' parameter
  $customer = CustomerPeer::retrieveByPk($this->getRequestParameter('id'));
 
  // class initialization
  $mail = new sfMail();
  $mail->setCharset('utf-8');      
 
  // definition of the required parameters
  $mail->setSender('webmaster@my-company.com', 'My Company webmaster');
  $mail->setFrom('webmaster@my-company.com', 'My Company webmaster');
  $mail->addReplyTo('webmaster_copy@my-company.com');
 
  $mail->addAddress($customer->getEmail());
 
  $mail->setSubject('Your password request');
 
  $this->password = $customer->getPassword();
  $this->mail = $mail;    
}

Notice that the action doesn't need to call the send() method for the sfMail object; being called by a sendEmail(), it knows that it needs to finish by sending its $this->mail sfMail object. And where is the mail? will you ask. That's the beauty of the MVC separation: the body of the mail itself is to be written in the template sendPasswordSuccess.php:

Dear customer,
 
You are so absentminded. Next time, try to remember your password:
<?php echo $password ?>
 
Regards,
The My Company webmaster

note

If the sendPassword action ever determines that the email doesn't have to be sent, it can still abort the emailing process by returning sfView::NONE, just like a regular action.

Mailer configuration

If you use the alternate action method, you can (you don't have to) set the mailer and activate it environment by environment through a configuration file.

Create a mailer.yml configuration file in the modules/mail/config/ directory with:

dev:
  deliver:    off

all:
  mailer:     sendmail

This stipulates the mailer program to be used to send mails, and deactivates the sending of mails in the development environment.

Send HTML email

Most of the time, emails are to be sent in HTML format, or even in a multipart format (enclosing both HTML and text format). To handle it directly with the sfMail object, use the setContentType() method and specify an alternate body:

$mail->setContentType('text/html');
$mail->setBody('
<p>Dear customer</p>,
<p>
  You are so <i>absentminded</i>. Next time, try to remember your password:<br>
  <b>'.$customer->getPassword().'</b>
</p>
<p>
  Regards,<br>
  The My Company webmaster
</p>');    
$mail->setAltBody('
Dear customer,
 
You are so absentminded. Next time, try to remember your password:
'.$customer->getPassword().'
 
Regards,
The My Company webmaster');    

If you use an alternate action, you will just need to use an alternate template ending with .altbody.php. Symfony will automatically add it as alternate body:

// in sendPasswordSuccess.php
<p>Dear customer</p>,
<p>
  You are so <i>absentminded</i>. Next time, try to remember your password:<br>
  <b><?php echo $password ?></b>
</p>
<p>
  Regards,<br>
  The My Company webmaster
</p>
 
// in sendPasswordSuccess.altbody.php
Dear customer,
 
You are so absentminded. Next time, try to remember your password:
<?php echo $password ?>
 
Regards,
The My Company webmaster

note

If you just use an HTML version without altbody template, you will need to set the content type to text/html in the sendPassword action.

Embed images

HTML emails can contain images directly embedded in the body. To add an embedded image, use the addEmbeddedImage() method of the sfMail object:

$mail->addEmbeddedImage(sfConfig::get('sf_web_dir').'/legacy/images/my_company_logo.gif', 'CID1', 'My Company Logo', 'base64', 'image/gif');

The first argument is the path to the image, the second is a reference of the image that you can use in the template to embed it:

// in sendPasswordSuccess.php
<p>Dear customer</p>,
<p>
  You are so <i>absentminded</i>. Next time, try to remember your password:<br>
  <b><?php echo $password ?></b>
</p>
<p>
  Regards,<br>
  The My Company webmaster
  <img src="cid:CID1" />
</p>

tip

You can also embed imaged directly from the template, without any code in the action. Refer to the embedded_image() helper description in the code snippets section.

Attachments

Attaching a document to a mail is as simple as you would expect it to be:

// document attachment
$mail->addAttachment(sfConfig::get('sf_data_dir').'/MyDocument.doc');
// string attachment
$mail->addStringAttachment('this is some cool text to embed', 'file.txt');

Email addresses advanced syntax

In addition to recipients, emails often need to be sent as carbon copy ('cc:') or blind carbon copy ('bcc:'). Here is how to do this with sfMail:

$mail->addAddress($customer->getEmail());
$mail->addCc('carbon_copy@my-companyt.com ');    
$mail->addBcc('blind_carbon_copy@my-company.com');

The sfMail methods used to set emails (setSender(), setFrom(), addReplyTo(), addAddress(), addCc(), addBcc()) can use two syntaxes:

$mail->setFrom('me@symfony-project.com', 'Symfony');
// is equivalent to
$mail->setFrom('me@symfony-project.com <Symfony>');

In addition, to minimize the code in case of multiple recipients, sfMail has an addAddresses() method:

$mail->addAddress('client1@client.com <Jules>');
$mail->addAddress('client2@client.com <Jim>');
// is equivalent to
$mail->addAddresses(array('client1@client.com <Jules>', 'client2@client.com <Jim>'));

sfMail methods

Once you've built your sfMail object, you might want to check its content. Fortunately, all the setter methods described above have an equivalent getter method, and you can clear the properties previously set:

setCharset($charset)
getCharset()
setContentType($content_type)
getContentType()
setPriority($priority)
getPriority()
setEncoding($encoding)
getEncoding()
setSubject($subject)
getSubject()
setBody($body)
getBody()
setAltBody($text)
getAltBody()
setMailer($type = 'mail')
getMailer()
setSender($address, $name = null)
getSender()
setFrom($address, $name = null)
getFrom()
addAddresses($addresses)
addAddress($address, $name = null)
addCc($address, $name = null)
addBcc($address, $name = null)
addReplyTo($address, $name = null)
clearAddresses()
clearCcs()
clearBccs()
clearReplyTos()
clearAllRecipients()
addAttachment($path, $name = '', $encoding = 'base64', $type = 'application/octet-stream')
addStringAttachment($string, $filename, $encoding = 'base64', $type = 'application/octet-stream')
addEmbeddedImage($path, $cid, $name = '', $encoding = 'base64', $type = 'application/octet-stream')
setAttachments($attachments)
clearAttachments()
addCustomHeader($name, $value)
clearCustomHeaders()
setWordWrap($wordWrap)
getWordWrap()

Sending emails from outside an action

If you want to send emails outside of an action (for instance in a batch process), you don't have any actions object to call the sendEmail() method on. Fortunately, this method is also available on the sfController object. So here is how you would call an emailing action in a batch script:

<?php
 
define('SF_ROOT_DIR',    realpath(dirname(__FILE__).'/..'));
define('SF_APP',         'myapp');
define('SF_ENVIRONMENT', 'test');
define('SF_DEBUG',       false);
 
require_once(SF_ROOT_DIR.DIRECTORY_SEPARATOR.'apps'.DIRECTORY_SEPARATOR.SF_APP.DIRECTORY_SEPARATOR.'config'.DIRECTORY_SEPARATOR.'config.php');
 
$raw_email = sfContext::getInstance()->getController()->sendEmail('mail', 'sendPassword');