Quantcast
Channel: Live News for Yii Framework
Viewing all articles
Browse latest Browse all 2941

[Wiki] Simple Mail Queue

$
0
0

Overview

This tutorial shows how to create a simple mail queue. It is usually run from a cron job but can be run from the command line too.

The basic idea is to create a complete mail message and store it a Db table along with all info necessary for sending valid emails (to_email, from_email, from_name, subject etc.)

Database Structure

A simple table for holding mail queue items.

-- 
-- Structure for table `tbl_email_queue`
-- 
DROP TABLE IF EXISTS `tbl_email_queue`;
CREATE TABLE IF NOT EXISTS `tbl_email_queue` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `from_name` varchar(64) DEFAULT NULL,
  `from_email` varchar(128) NOT NULL,
  `to_email` varchar(128) NOT NULL,
  `subject` varchar(255) NOT NULL,
  `message` text NOT NULL,
  `max_attempts` int(11) NOT NULL DEFAULT '3',
  `attempts` int(11) NOT NULL DEFAULT '0',
  `success` tinyint(1) NOT NULL DEFAULT '0',
  `date_published` datetime DEFAULT NULL,
  `last_attempt` datetime DEFAULT NULL,
  `date_sent` datetime DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `to_email` (`to_email`)
) ENGINE=MyISAM AUTO_INCREMENT=5 DEFAULT CHARSET=latin1;

MailQueue Model

CRUD operations for tbl_mail_queue

<?php
/**
 * This is the model class for table "{{email_queue}}".
 *
 * The followings are the available columns in table '{{email_queue}}':
 * @property integer $id
 * @property string $from_name
 * @property string $from_email
 * @property string $to_email
 * @property string $subject
 * @property string $message
 * @property integer $max_attempts
 * @property integer $attempts
 * @property integer $success
 * @property string $date_published
 * @property string $last_attempt
 * @property string $date_sent
 */
class EmailQueue extends CActiveRecord
{
 
    /**
        * Returns the static model of the specified AR class.
        * @param string $className active record class name.
        * @return EmailQueue the static model class
        */
    public static function model($className=__CLASS__)
    {
        return parent::model($className);
    }
 
    /**
        * @return string the associated database table name
        */
    public function tableName()
    {
        return '{{email_queue}}';
    }
 
    /**
        * @return array validation rules for model attributes.
        */
    public function rules()
    {
        // NOTE: you should only define rules for those attributes that
        // will receive user inputs.
        return array(
            array('from_email, to_email, subject, message', 'required'),
            array('max_attempts, attempts, success', 'numerical', 'integerOnly' => true),
            array('from_name', 'length', 'max' => 64),
            array('from_email, to_email', 'length', 'max' => 128),
            array('subject', 'length', 'max' => 255),
            array('date_published, last_attempt, date_sent', 'safe'),
            // The following rule is used by search().
            // Please remove those attributes that should not be searched.
            array('id, from_name, from_email, to_email, subject, message, max_attempts, attempts, success, date_published, last_attempt, date_sent', 'safe', 'on' => 'search'),
        );
    }
 
    /**
        * @return array relational rules.
        */
    public function relations()
    {
        // NOTE: you may need to adjust the relation name and the related
        // class name for the relations automatically generated below.
        return array(
        );
    }
 
    /**
        * @return array customized attribute labels (name=>label)
        */
    public function attributeLabels()
    {
        return array(
            'id' => 'ID',
            'from_name' => 'From Name',
            'from_email' => 'From email',
            'to_email' => 'To email',
            'subject' => 'Subject',
            'message' => 'Message',
            'max_attempts' => 'Max Attempts',
            'attempts' => 'Attempts',
            'success' => 'Success',
            'date_published' => 'Date Published',
            'last_attempt' => 'Last Attempt',
            'date_sent' => 'Date Sent',
        );
    }
 
    /**
        * Retrieves a list of models based on the current search/filter conditions.
        * @return CActiveDataProvider the data provider that can return the models based on the search/filter conditions.
        */
    public function search()
    {
        // Warning: Please modify the following code to remove attributes that
        // should not be searched.
 
        $criteria = new CDbCriteria;
 
        $criteria->compare('id', $this->id);
        $criteria->compare('from_name', $this->from_name, true);
        $criteria->compare('from_email', $this->from_email, true);
        $criteria->compare('to_email', $this->to_email, true);
        $criteria->compare('subject', $this->subject, true);
        $criteria->compare('message', $this->message, true);
        $criteria->compare('max_attempts', $this->max_attempts);
        $criteria->compare('attempts', $this->attempts);
        $criteria->compare('success', $this->success);
        $criteria->compare('date_published', $this->date_published, true);
        $criteria->compare('last_attempt', $this->last_attempt, true);
        $criteria->compare('date_sent', $this->date_sent, true);
 
        return new CActiveDataProvider($this, array(
                'criteria' => $criteria,
            ));
    }
 
}
?>

Console Command

Retrieves a list of active mail queue objects and fires off the emails

<?php
/**
 * MailQueueCommand class file.
 *
 * @author Matt Skelton
 * @date 26-Jun-2011
 */
 
/**
 * Sends out emails based on the retrieved EmailQueue objects. 
 */
class MailQueueCommand extends CConsoleCommand
{
 
    public function run($args)
    {
        $criteria = new CDbCriteria(array(
                'condition' => 'success=:success AND attempts < max_attempts',
                'params' => array(
                    ':success' => 0,
                ),
            ));
 
        $queueList = EmailQueue::model()->findAll($criteria);
 
        /* @var $queueItem EmailQueue */
        foreach ($queueList as $queueItem)
        {
            $message = new YiiMailMessage();
            $message->setTo($queueItem->to_email);
            $message->setFrom(array($queueItem->from_email => $queueItem->from_name));
            $message->setSubject($queueItem->subject);
            $message->setBody($queueItem->message, 'text/html');
 
            if ($this->sendEmail($message))
            {
                $queueItem->attempts = $queueItem->attempts + 1;
                $queueItem->success = 1;
                $queueItem->last_attempt = new CDbExpression('NOW()');
                $queueItem->date_sent = new CDbExpression('NOW()');
 
                $queueItem->save();
            }
            else
            {
                $queueItem->attempts = $queueItem->attempts + 1;
                $queueItem->last_attempt = new CDbExpression('NOW()');
 
                $queueItem->save();
            }
        }
    }
 
    /**
        * Sends an email to the user.
        * This methods expects a complete message that includes to, from, subject, and body
        *
        * @param YiiMailMessage $message the message to be sent to the user
        * @return boolean returns true if the message was sent successfully or false if unsuccessful
        */
    private function sendEmail(YiiMailMessage $message)
    {
        $sendStatus = false;
 
        if (Yii::app()->mail->send($message) > 0)
            $sendStatus = true;
 
        return $sendStatus;
    }
 
}
?>

Usage

Now that we've got our structure setup, we can simply start creating MailQueue objects. This can be implemented in a behavior, event handlers, or simply in a controller's action.

Below, I'm creating a MailQueue object in a model's afterSave event handler.

// Typical usage in a controller or model
public function afterSave()
{
    $queue = new EmailQueue();
    $queue->to_email = 'bill_hicks@afterlife.com';
    $queue->subject = "Mall Kids Are People Too, Damnit!";
    $queue->from_email = Yii::app()->params['adminEmail'];
    $queue->from_name = Yii::app()->name;
    $queue->date_published = new CDbExpression('NOW()');
    $queue->message = Yii::app()->controller->renderPartial('//mail/sarcastic/notify', array(
        ...
    ), true); // Make sure to return true since we want to capture the output
 
    $queue->save();
 
    parent::afterSave();
}

That's it. Now you can point your CRON/Task Scheduler at the command and watch the electromagnetic mail fly!

Feedback welcome.


Viewing all articles
Browse latest Browse all 2941

Trending Articles