Simple Mail Queue

You are viewing revision #4 of this wiki article.
This version may not be up to date with the latest version.
You may want to view the differences to the latest version or see the changes made in this revision.

« previous (#3)next (#5) »

  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: 28 498 times
Version: Unknown (update)
Category: Tutorials
Written by: waterloomatt
Last updated by: Bizley
Created on: May 28, 2013
Last updated: 8 years ago
Update Article

Revisions

View all history

Related Articles