Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
215 changes: 111 additions & 104 deletions MailQueue.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,24 +19,24 @@
* Configuration is the same as in `yii\switmailer\Mailer` with some additional properties to control the mail queue
*
* ~~~
* 'components' => [
* ...
* 'mailqueue' => [
* 'class' => 'nterms\mailqueue\MailQueue',
* 'table' => '{{%mail_queue}}',
* 'mailsPerRound' => 10,
* 'maxAttempts' => 3,
* 'transport' => [
* 'class' => 'Swift_SmtpTransport',
* 'host' => 'localhost',
* 'username' => 'username',
* 'password' => 'password',
* 'port' => '587',
* 'encryption' => 'tls',
* ],
* ],
* ...
* ],
* 'components' => [
* ...
* 'mailqueue' => [
* 'class' => 'nterms\mailqueue\MailQueue',
* 'table' => '{{%mail_queue}}',
* 'mailsPerRound' => 10,
* 'maxAttempts' => 3,
* 'transport' => [
* 'class' => 'Swift_SmtpTransport',
* 'host' => 'localhost',
* 'username' => 'username',
* 'password' => 'password',
* 'port' => '587',
* 'encryption' => 'tls',
* ],
* ],
* ...
* ],
* ~~~
*
* @see http://www.yiiframework.com/doc-2.0/yii-swiftmailer-mailer.html
Expand All @@ -48,90 +48,97 @@
*/
class MailQueue extends Mailer
{
const NAME = 'mailqueue';

/**
* @var string message default class name.
*/
public $messageClass = 'nterms\mailqueue\Message';

/**
* @var string the name of the database table to store the mail queue.
*/
public $table = '{{%mail_queue}}';

/**
* @var integer the default value for the number of mails to be sent out per processing round.
*/
public $mailsPerRound = 10;

/**
* @var integer maximum number of attempts to try sending an email out.
*/
public $maxAttempts = 3;


/**
* @var boolean Purges messages from queue after sending
*/
public $autoPurge = true;

/**
* Initializes the MailQueue component.
*/
public function init()
{
parent::init();
}

/**
* Sends out the messages in email queue and update the database.
*
* @return boolean true if all messages are successfully sent out
*/
public function process()
{
if (Yii::$app->db->getTableSchema($this->table) == null) {
throw new \yii\base\InvalidConfigException('"' . $this->table . '" not found in database. Make sure the db migration is properly done and the table is created.');
}

$success = true;

$items = Queue::find()->where(['and', ['sent_time' => NULL], ['<', 'attempts', $this->maxAttempts], ['<=', 'time_to_send', date('Y-m-d H:i:s')]])->orderBy(['created_at' => SORT_ASC])->limit($this->mailsPerRound);
foreach ($items->each() as $item) {
if ($message = $item->toMessage()) {
$attributes = ['attempts', 'last_attempt_time'];
if ($this->send($message)) {
$item->sent_time = new \yii\db\Expression('NOW()');
$attributes[] = 'sent_time';
} else {
$success = false;
}

$item->attempts++;
$item->last_attempt_time = new \yii\db\Expression('NOW()');

$item->updateAttributes($attributes);
}
}

// Purge messages now?
if ($this->autoPurge) {
$this->purge();
}

return $success;
}


/**
* Deletes sent messages from queue.
*
* @return int Number of rows deleted
*/

public function purge()
{
return Queue::deleteAll('sent_time IS NOT NULL');
}
const NAME = 'mailqueue';

/**
* @var string message default class name.
*/
public $messageClass = 'nterms\mailqueue\Message';

/**
* @var string the name of the database table to store the mail queue.
*/
public $table = '{{%mail_queue}}';

/**
* @var integer the default value for the number of mails to be sent out per processing round.
*/
public $mailsPerRound = 10;

/**
* @var integer maximum number of attempts to try sending an email out.
*/
public $maxAttempts = 3;


/**
* @var boolean Purges messages from queue after sending
*/
public $autoPurge = true;

/**
* Initializes the MailQueue component.
*/
public function init()
{
parent::init();
}

/**
* Sends out the messages in email queue and update the database.
*
* @return boolean true if all messages are successfully sent out
*/
public function process()
{
if (Yii::$app->db->getTableSchema($this->table) == null) {
throw new \yii\base\InvalidConfigException('"' . $this->table . '" not found in database. Make sure the db migration is properly done and the table is created.');
}

$success = true;

$items = Queue::find()
->where(['and', ['sent_time' => NULL], ['<', 'attempts', $this->maxAttempts], ['<=', 'time_to_send', date('Y-m-d H:i:s')]])
->orderBy([
'priority' => SORT_ASC,
'created_at' => SORT_ASC
])
->limit($this->mailsPerRound);

foreach ($items->each() as $item) {
if ($message = $item->toMessage()) {
$attributes = ['attempts', 'last_attempt_time'];
if ($this->send($message)) {
$item->sent_time = new \yii\db\Expression('NOW()');
$attributes[] = 'sent_time';
} else {
$success = false;
}

$item->attempts++;
$item->last_attempt_time = new \yii\db\Expression('NOW()');

$item->updateAttributes($attributes);
}
}

// Purge messages now?
if ($this->autoPurge) {
$this->purge();
}

return $success;
}


/**
* Deletes sent messages from queue.
*
* @return int Number of rows deleted
*/

public function purge()
{
return Queue::deleteAll('sent_time IS NOT NULL');
}
}
23 changes: 16 additions & 7 deletions Message.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,14 @@
*/
class Message extends \yii\swiftmailer\Message
{

public $priority = 1000;

public function setQueuePriority($priority = 1000)
{
$this->priority = $priority;
}

/**
* Enqueue the message storing it in database.
*
Expand All @@ -25,16 +33,17 @@ class Message extends \yii\swiftmailer\Message
*/
public function queue($time_to_send = 'now')
{
if($time_to_send == 'now') {
if ($time_to_send == 'now') {
$time_to_send = time();
}

$item = new Queue();

$item->subject = $this->getSubject();
$item->attempts = 0;
$item->swift_message = base64_encode(serialize($this));
$item->time_to_send = date('Y-m-d H:i:s', $time_to_send);
$item = new Queue([
'priority' => $this->priority,
'subject' => $this->getSubject(),
'attempts' => 0,
'swift_message' => base64_encode(serialize($this)),
'time_to_send' => date('Y-m-d H:i:s', $time_to_send),
]);

return $item->save();
}
Expand Down
21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,27 @@ Yii::$app->mailqueue->compose('contact/html')
->send();
```

Priorities
----------

You can set a priority to the message:

```php
Yii::$app->mailqueue->compose('contact/html')
->setQueuePriority(500)
->setFrom('from@domain.com')
->setTo($form->email)
->setSubject($form->subject)
->setTextBody($form->body)
->queue();
```

Default priority is 1000 so passing any lower number will make that that emails gets sent before others.

In the same way, if you pass a number bigger than 1000, those messages are going to have very low priority in the queue.

Why 1000 as default? Just to give enough room to accomodate at most 999 other priorities.

License
-------

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

use yii\db\Migration;
use nterms\mailqueue\MailQueue;

/**
* Handles adding columns to table `{{%mail_queue}}`.
*/
class m210718_062540_add_priority_column_to_mail_queue_table extends Migration
{
/**
* {@inheritdoc}
*/
public function safeUp()
{
$this->addColumn(Yii::$app->get(MailQueue::NAME)->table, 'priority', $this->integer()->notNull()->defaultValue(1000));
}

/**
* {@inheritdoc}
*/
public function safeDown()
{
$this->dropColumn(Yii::$app->get(MailQueue::NAME)->table, 'priority');
}
}
3 changes: 2 additions & 1 deletion models/Queue.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
* @property integer $sent_time
* @property string $time_to_send
* @property string $swift_message
* @property integer $priority
*/
class Queue extends ActiveRecord
{
Expand Down Expand Up @@ -51,7 +52,7 @@ public function behaviors()
public function rules()
{
return [
[['created_at', 'attempts', 'last_attempt_time', 'sent_time'], 'integer'],
[['created_at', 'attempts', 'last_attempt_time', 'sent_time', 'integer'], 'integer'],
[['time_to_send', 'swift_message'], 'required'],
[['subject'], 'safe'],
];
Expand Down