Symfony World blog is not maintained anymore. Check new sys.exit() programming blog.
Showing posts with label mail. Show all posts
Showing posts with label mail. Show all posts

simple mailing system with symfony - part I

This article is the beginning of a tutorial presenting an easy way to implement database-templated-mailing system for symfony projects.


the basics


The basic version uses 2 tables: mail_queue and mail_template. For each type of mails, one template object is created.


mail_template

name, description - name & description of the template
template_code - (HTML, including variables marked as {variable})
template_data - what variables are defined, just a description, no calculations performed


For each single mail to be sent, one queue object is created.


mail_queue

template_id - which template to use
mail_data - serialized array: variables=>values
mail_recipent - formatted/serialized recipent information
mail_author - as above
mail_subject - string
send_at - time that this email should be sent
sent - boolean, already sent


Table structure for mail_template and mail_queue tables

E-mail is sent when sent=0 (and marked 1 afterwards, not to be sent again; in case of errors - mark as 2) and when actual time has gone beyond send_at. send_at column is especially useful, as you may create emails in a long time advance. For example, you want to send a reminder E-mail: create a new MailQueue object with its datetime value equal to current timestamp + two weeks. This would make the E-mail to be sent in two weeks time from now.


example usage


What kind of mails can we define? It depends on your project functionalities. For example, if your project is a stock managing software that stores information about products and their states, you can send important E-mails when one of the following situations take place:

  • status of a product is changing - other employees shall be informed about that,
  • quantity of a product reached alarming level - supply needed immediately,
  • inform customers when product becomes available again,
  • and so on...
A template object is created for each of the situations defined above. There's a MailTools class with static methods processing mail sending. Just like notifyProductStatusHasChanged method, taking two parameters: Product and status change. This method creates a mail_queue instance, defining specific data for this type of E-mail, using Article object passed as the parameter.


some details on implementation


Symfony Swift Mailer integration is provided with a ready to use mail queueing system. However, I decided to create a special task, defined to send E-mails that are marked with sent=0. The code is pretty obvious:

$mails = Doctrine::getTable('MailQueue')
  ->getMails2bSentQuery()
  ->execute();
 
foreach($mails as $m)
{
  $m->send();
}

Above task is run by a cron job (every few minutes). getMails2bSentQuery method looks for MailQueue objects that have sent=0 and send_at <= now. For each of those objects, the template code (HTML) is filled with variable data and sent afterwards. The mail data is unserialized first and each variable is injected into the HTML code using str_replace function:



protected function generateContent()
{
  $code = $this->getTemplate()->getTemplateCode();
  $data = unserialize($this->getMailData());
  foreach($data as $key => $value)
    $code = str_replace('{'.$key.'}', $value, $code);
  return $code;
}

Each mail template is defined using HTML/CSS code. It works similar to smarty template engine. Just define a list of variables of and all occurences of {VARIABLE} will be replaced with the parameter value you pass.


to be continued

This was just a brief overview. In the near future, precise code examples will be provided in tutorial's part 2.

Mail testing configuration in symfony

By default, the symfony mail delivery strategy is set to none. That means, that calling send method upon sfMailer object will perform no action.


Configuring mail delivery_strategy


All possible delivery strategies are described in the symfony docs. A good solution is to edit factories.yml in all application config directories and set production environment to realtime and development environment to single_address. The developer enters an E-mail address he's got access to, puts it in the delivery_address option and all mails sent from the project will be delivered to that address (so there's no worry that a customer/user will receive test E-mail). Put the following lines into factories.yml file to define mail configuration:

prod:
  mailer:
    param:
      delivery_strategy: realtime
dev:
  mailer:
    param:
      delivery_strategy: single_address
      delivery_address: your@address.com
Once we've set those values (even in the very beginning of a project development), we don't have to carry about mail configuration, hence symfony will carry out all mails to the correct addresses when working in the production environment.

Symfony newsletter using Swift mailer

Scene from "Postman Pat" by John Cunliffe & Ivor Wood

introduction

I had a task to implement a newsletter system for a sf 1.4 project. It was quite simple: an anonymous user from outside can subscribe to the newsletter (storing his E-mail address in the database). The site admin can create a newsletter article (with simple properties like title and content). When the send button is pressed, this article is sent to all subscribed E-mails.

output escaping

The newsletter article content value is modified using javascript rich editor such as tinyMCE or FCK Editor, so it has HTML tags like <p>. I forced a problem that either Symfony or Swift was escaping all those HTML tags. I was trying to find solution using methods like getRawValue, getRaw or anything like that, but it turned out that it was Swift Mailer who was the cause of the problem. Following this small tutorial, I needed to add only one line of code:

$message->setContentType("text/html");
Now all formatting was shown properly.

final code solution

I hope that some of you may find this piece of code useful:

public function executeListSend()
{
  $newsletter = $this->getRoute()->getObject();
  if ($newsletter->getSent())
  {
    $this->getUser()->setFlash('notice',
      'The selected newsletter has already been sent.');
  }
  else
  {
    $address_collection = Doctrine::getTable('Address')
      ->getAllAddressesQuery()
      ->fetchArray();
 
    $addresses = array();
    foreach($address_collection as $address)
      $addresses[] = $address['address'];
 
    $message = $this->getMailer()->compose(
      array('name.surname@gmail.com' => 'sitename'),
      $addresses,
      $newsletter->getTitle(),
      $newsletter->getContent()
    );
    $message->setContentType("text/html");
    $this->getMailer()->send($message);
 
    $newsletter->setSent(true);
    $newsletter->save();
 
    $this->getUser()->setFlash('notice',
      'The selected newsletter has been sent successfully.');
  }
  $this->redirect('@newsletter');
}
Implementing a newsletter with Symfony & Swift is really easy!