1

Is there a way to parse a variable to a queue job? I want to re-send some Craft commerce emails via a cron job, so have created a console controller for the cron job to run a command against. This then will get all the customers to email and create a queued job (I'd like the jobs to run automatically somehow). To save having one big job to email 100's of customers, I thought to create 1 job per customer ... only way I can think is to have the controller parse the customer email through each new job it creates.

I'm currently testing with sending a email to a sandbox email account in 1 queued job that loops through a range of 1-10. This method is becoming too unreliable where I might get emails 1-5 out of 10, or 1-5 then 15-20 in a loop of 20. The test I've just run only sent 18-20 out of 20. When I've tested with creating a job per email, I get a consistent amount of emails.

2

Yes, you can pass through the customer ID to your queue job, then the job itself can look up the customer and send the email.

Here's a super-simple example console controller action method that passes through a user ID to a queue job class called SendEmail:

public function actionSend()
{
    $userIds = User::find()
        ->status(User::STATUS_ACTIVE)
        ->ids();
    
    foreach ($userIds as $userId) {
        Craft::$app->getQueue()->delay(0)->push(new SendEmail([
            'userId' => $userId
        ])); 
    }
}

Then here's the SendEmail job class. This has a property called userId which represents the variable that's passed in when the new instance of the SendEmail class is created in the code example above. You can use that userId property to fetch the User object, and then send an email:

class SendEmail extends BaseJob
{
    // Public Properties
    // =========================================================================

    /**
     * @var string
     */
    public $userId;

    // Public Methods
    // =========================================================================

    /**
     * @param QueueInterface|Queue $queue
     * @return void
     */
    public function execute($queue)
    {  
        $user = Craft::$app->getUsers()->getUserById($this->userId);

        if ($user) {
            Craft::$app->getMailer()
                ->composeFromKey('my-plugin_email_key, [
                    'firstName' => $user->firstName,
                ])
                ->setTo($user)
                ->send();
        }

        $this->setProgress($queue, 1);
    }

    // Protected Methods
    // =========================================================================

    /**
     * @return string
     */
    protected function defaultDescription(): string
    {
        return Craft::t(
            'my-plugin',
            'Sending email for user Id {userId}',
            [
                'userId' => $this->userId,
            ]
        );
    }
}

I've left out the namespace and use statements etc., but hopefully that gives you an idea of how this works.

You'll need to register the email you want to send in your plugin/module's base class. I do this via a _registerEmailMessages class called from the plugin/module's init method:

public function init()
{
    parent::init();
    self::$plugin = $this;

    // Do a bunch of init stuff
    $this->_registerEmailMessages();
}

private function _registerEmailMessages()
{
    Event::on(
        SystemMessages::class,
        SystemMessages::EVENT_REGISTER_MESSAGES,
        function(RegisterEmailMessagesEvent $event) {
            $event->messages = array_merge($event->messages, [
                [
                    'key' => 'my-plugin_email_key',
                    'heading' => Craft::t('my-plugin', 'my-plugin_email_key_heading'),
                    'subject' => Craft::t('my-plugin', 'my-plugin_email_key_subject'),
                    'body' => Craft::t('my-plugin', 'my-plugin_email_key_body'),
                ],
           ]);
        }
    );
}
2
  • I just came on to post my own answer, but your's is definitely better presented! Can I check the line: Craft::$app->getQueue()->delay(0)->push(new SendEmail([ 'userId' => $userId ])); Would this process the job when it's added?
    – Jaytrix
    Jun 23 at 15:36
  • That depends on how you have your queue set up. I use the Async Queue plugin from Oliver Stark (github.com/ostark/craft-async-queue) to run the queue using background processes, so in my setup it wouldn't run straight away. In a stock setup, I believe it would be processed as soon as the next request (i.e. page load) to Craft was made. Jun 24 at 7:29

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.