2

I have my plugin working now so that I can email users who's accounts will expire in 7 days time. The users are being selected correctly and the emails are being sent as expected. However, the tasks don't seem to be running in the background. I was expecting that when I hit the controller the task will be triggered and the page would immediately return saying "emails sent". Instead it seems it waits until the emails are sent before returning "emails sent". I'm worried that if the number of emails is large the page will timeout before it completes. It was my understanding that tasks run as a background process but may I have miss understood. Can anyone explain them to me in more detail?

For reference here is the controller and the service of my plugin:

class AcmeNotifications_NotificationsController extends BaseController
{

// let anyone run the controller actions
protected $allowAnonymous = array('actionSendEmail');

public function actionSendEmail()
{
    craft()->acmeNotifications_notifications->sendReminderEmail();

    //Make sure we kick the task off

    if (!craft()->tasks->isTaskRunning())
    {
        $task = craft()->tasks->getNextPendingTask();

        if ($task)
        {
            craft()->tasks->runPendingTasks();
        }
    }

    craft()->end();
}
}

and:

class AcmeNotifications_NotificationsService extends BaseApplicationComponent
{
public function sendReminderEmail()
{
    //Get all users whos account will expire in 7 days
    $user_criteria = craft()->elements->getCriteria(ElementType::User);

    $user_criteria->userExpiryDate = date('Y-m-d',strtotime("+7 days"));

    $users = $user_criteria->find();



    if($users){
        //Create array of user email addresses
        $user_emails = array();
        foreach ($users as $user) {
            $user_emails[] = array("to" => $user->email);
        }

        //Create an EmailModel
        $email = new EmailModel;

        //Set the global attributes 
        $email->fromName = 'Joe Blogs';
        $email->fromEmail = 'joe.blogs@example.com';
        $email->subject     = 'Your account will expire in 7 days';
        $email->htmlBody    = 'The message';

        //Create the RecipientsModel and add the user emails
        $recipients = new Mailer_RecipientsModel();
        $recipients->recipients = $user_emails;

        if( $recipients->validate()){
            //Create mailer task
            craft()->mailer_main->newMailer($recipients, $email); 
        }

        echo "emails sent";
    }
    else{
        echo "no emails to send";
    }
}


} 
2

You are right about the Tasks running in the background. If the Task is just sending a few mails, it might be done before your site is loaded, thus creating this impression.

Check:

  • Change the Batch Size of the Mailer plugin to 1 and the waiting time to 120 seconds.
  • Change the $user_criteria to fetch 2 users.
  • Run your controller

It should now take about 2 minutes for the Task to finish, if your site loads faster it was just an impression.


Code:

You should however separate your code a bit more, because right now you are mixing Controllers and Services.

Your Service shouldn't output anything, it should just return a value, (e.g. boolen), and your Controller should then output the message:

Service {
    //Create Task etc...

    if ($itWorked == true) {
       return true;
    }
    else {
       return false;
    }
}

/* ------------------------ */

Controller {
    if ( FunctionService() ) {
       echo 'It worked';
    }
    else {
       echo 'Something went wrong';
    }
}

Also the "Make sure we kick the task off"-part, should go into your Service.

P.S. I see you are using my plugin :), if you need any help, create an issue over on github.

4
  • Thanks victor for the reply and for explaining to me about the best practices for controllers and services. Another piece of the puzzle just clicked into place :) Also, thanks for a great plugin, it works like a charm, very pleased with it. I'll be sure to head over github if I run into any issues in the future. Thanks again. – Dave Coggins Jul 25 '14 at 13:49
  • Hi Victor, I've just tried out the settings you suggested and you are right, the emails are being sent as a background process as they should be, what I was experiencing was just an impression. I have noticed though the when I hit the controller I get a 504 Gateway Time-out before the controller returns with "emails sent". I guess this is not a massive deal because I'm going to be hitting this from a CRON job so as long as the task is triggered all should be well. I was wondering, do you know if it is possible to trigger the task from the command line rather than through the browser? – Dave Coggins Jul 25 '14 at 14:06
  • I don't know why you get the time-out, maybe the output in the service causes this, but I'm not sure. I haven't used CRON yet, but the best way would be to trigger the Controller which then starts the Service and Task. – Victor In Jul 25 '14 at 14:11
  • I tracked down 504 gateway time-out to be to do with my nginx config. I fixed it by adding fastcgi_read_timeout 300; to the config. – Dave Coggins Jul 26 '14 at 0:51

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.