Simple Mail Queue

  1. Overview
  2. Database Structure
  3. [sql]
  4. -- Structure for table tbl_email_queue
  5. MailQueue Model
  6. Console Command
  7. Usage

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. ~~~

[sql]

-- Structure for table tbl_email_queue

DROP TABLE IF EXISTS tbl_email_queue; CREATE TABLE IF NOT EXISTS tbl_email_queue ( id int(10) unsigned 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 tinyint(3) unsigned NOT NULL DEFAULT '3', attempts tinyint(3) unsigned 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 DEFAULT CHARSET=utf8_general_ci; ~~~

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()
    {
        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'),

            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 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',
        );
    }
}
?>

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.

8 0
14 followers
Viewed: 19 538 times
Version: 1.1
Category: Tutorials
Written by: waterloomatt
Last updated by: Bizley
Created on: May 28, 2013
Last updated: 3 years ago
Update Article

Revisions

View all history

Related Articles