Skip to content
On this page

Job Queues

Sometimes you have tasks that are better run asynchronously. For example, you might need to perform some expensive processing or a slow api call that would take too long for a client facing web request.

Forme has a basic no frills job queue system for exactly this situation. This is nowhere near as full featured as libraries like Bernard (although there's nothing to stop you implementing that within Forme) or Laravel's Queues, but it works fine for straight forward use cases.

The Job Class

Jobs live in app/Jobs, should implement Forme\Framework\Jobs\JobInterface, and need the Forme\Framework\Jobs\Queueable trait.

They're very similar to a controller, in that they have a public handle method, which will get called when it's next up in the queue.

php
<?php

declare(strict_types=1);

namespace NameSpace\CoolestPlugin\Jobs;

use Forme\Framework\Jobs\Queueable;
use Forme\Framework\Jobs\JobInterface;

final class FooBarJob implements JobInterface
{
    use Queueable;

    public function handle(array $args = []): ?string
    {
        // do something expensive here

        return "This will show up in the log file";
    }
}

Dispatching Jobs

To dispatch a job (in other words to add it to the queue to run as soon as possible), you have two options. You can either do this via the general Queue class, or via an instance of the specific Job class.

php
// via Forme\Framework\Jobs\Queue
// in this case, you need to pass an associative array with the class name and the arguments to pass in to the handle method
$queue->dispatch([
    'class' => 'NameSpace\\CoolestPlugin\\Jobs\\FooBarJob',
    'arguments' => ['foo' => 'bar']
]);

// via the specific job, e.g. NameSpace\CoolestPlugin\Jobs\FooBarJob
// you need to pass an array of arguments for the handle method
$fooBarJob->dispatch(['foo' => 'bar']);

Scheduling Jobs

To schedule a job, the process is similar, but you need to call the schedule method and pass in an additional next argument.

php
// via Forme\Framework\Jobs\Queue
// in this case, you need to pass an associative array with the class name and the arguments to pass in to the handle method
$queue->schedule([
    'class' => 'NameSpace\\CoolestPlugin\\Jobs\\FooBarJob',
    'arguments' => ['foo' => 'bar'],
    'next' => '1 hour' // something that carbon can parse, this will run after 1 hour has passed
 ]);

// via the specific job, e.g. NameSpace\CoolestPlugin\Jobs\FooBarJob
// same as queue, but you don't need to pass the class name
$fooBarJob->schedule([
    'arguments' => ['foo' => 'bar'],
    'next' => '1 hour' // something that carbon can parse, this will run after 1 hour has passed
 ]);

Recurring Jobs

For recurring jobs, you also need to call the schedule method but you pass in the frequency argument.

php
// via Forme\Framework\Jobs\Queue
// in this case, you need to pass an associative array with the class name and the arguments to pass in to the handle method
$queue->schedule([
    'class' => 'NameSpace\\CoolestPlugin\\Jobs\\FooBarJob',
    'arguments' => ['foo' => 'bar'],
    'next' => 'now', // something that carbon can parse, the first dispatch will run asap
    'frequency' => '1 week' // again something that carbon can parse, this will run once a week on the same day at the same time
 ]);

// via the specific job, e.g. NameSpace\CoolestPlugin\Jobs\FooBarJob
// same as queue, but you don't need to pass the class name
$fooBarJob->schedule([
    'arguments' => ['foo' => 'bar'],
    'next' => 'now', // something that carbon can parse, the first dispatch will run asap
    'frequency' => '1 week' // again something that carbon can parse, this will run once a week on the same day at the same time
 ]);

TIP

You can stop a recurring job with the stop method. Take a look below.

Starting (Unique) Recurring Jobs

You might need to start recurring jobs while ensuring that they are unique. For that you can use the start method.

Starting a job is very similar to scheduling, frequency is always required since by definition these are always recurring jobs. If a recurring job with the same class name already exists, nothing will happen.

php
// via Forme\Framework\Jobs\Queue
// in this case, you need to pass an associative array with the class name and the arguments to pass in to the handle method
$queue->start([
    'class' => 'NameSpace\\CoolestPlugin\\Jobs\\FooBarJob',
    'arguments' => ['foo' => 'bar'],
    'next' => 'now', // something that carbon can parse, the first dispatch will run asap
    'frequency' => '1 week' // again something that carbon can parse, this will run once a week on the same day at the same time
 ]);

// via the specific job, e.g. NameSpace\CoolestPlugin\Jobs\FooBarJob
// same as queue, but you don't need to pass the class name
$fooBarJob->start([
    'arguments' => ['foo' => 'bar'],
    'next' => 'now', // something that carbon can parse, the first dispatch will run asap
    'frequency' => '1 week' // again something that carbon can parse, this will run once a week on the same day at the same time
 ]);

WARNING

The uniqueness is based on a combination of the classname and the arguments, which has its own limitations. If you find that you need some other way of referencing the job, for example by an arbitrary id or some kind of content hash, you will have to implement that yourself.

Stopping Recurring Jobs

To stop a recurring job, call the stop method.

php
// via Forme\Framework\Jobs\Queue
// in this case, you need to pass an associative array with the class name and optionally the arguments
$queue->stop([
    'class' => 'NameSpace\\CoolestPlugin\\Jobs\\FooBarJob',
    'arguments' => ['foo' => 'bar'], // optional
 ]);

// via the specific job, e.g. NameSpace\CoolestPlugin\Jobs\FooBarJob
// same as queue, but you don't need to pass in the class name. You can optionall pass in the original arguments if you need to use them to identify the job
$fooBarJob->stop();
$fooBarJob->stop(['foo' => 'bar']); // optional arguments

TIP

The stop method will delete the first job it finds with that combination of class name and arguments. If you have multiple jobs with the same class name and arguments, you will need to call stop multiple times.

Named queues

Sometimes you want to have multiple queues and be able to reference them by name. You can pass in a queue name to any of the above commands.

php
// dispatch
$queue->dispatch([
    'class' => 'NameSpace\\CoolestPlugin\\Jobs\\FooBarJob',
    'arguments' => ['foo' => 'bar'],
    'queue_name' => 'baz',
]);
$fooBarJob->dispatch(['foo' => 'bar'], 'baz');

//schedule
$queue->schedule([
    'class' => 'NameSpace\\CoolestPlugin\\Jobs\\FooBarJob',
    'arguments' => ['foo' => 'bar'],
    'next' => 'now',
    'frequency' => '1 week',
    'queue_name' => 'baz',
 ]);

$fooBarJob->schedule([
    'arguments' => ['foo' => 'bar'],
    'next' => 'now',
    'frequency' => '1 week',
    'queue_name' => 'baz',
 ]);

 //start
 $queue->start([
    'class' => 'NameSpace\\CoolestPlugin\\Jobs\\FooBarJob',
    'arguments' => ['foo' => 'bar'],
    'next' => 'now',
    'frequency' => '1 week',
    'queue_name' => 'baz',
 ]);

// stop
$fooBarJob->start([
    'arguments' => ['foo' => 'bar'],
    'next' => 'now',
    'frequency' => '1 week',
    'queue_name' => 'baz',
 ]);

 //stop
 $queue->stop([
    'class' => 'NameSpace\\CoolestPlugin\\Jobs\\FooBarJob',
    'arguments' => ['foo' => 'bar'],
    'queue_name' => 'baz',
 ]);

$fooBarJob->stop(queueName: 'baz'); // you must use `queueName` explicitly if not passing in the arguments
$fooBarJob->stop(['foo' => 'bar'], 'baz');

The queue:run command

You can use the queue:run wrangle command to run the next job in the queue.

bash
# default queue
php wrangle queue:run

# named queue
php wrangle queue:run baz

Forme will check if there are any jobs that haven't either started running or been completed and are scheduled to run, pick the next one, and run it.

Setting up Cron

In order for this to happen automatically, you'll need to set up a cron job to regularly run the queue:run command.

sh
* * * * * cd /path-to-your-project && php wrangle queue:run >> /dev/null 2>&1

You would normally set this to run once a minute as above, but it depends what is relevant for your app.

Code Generation

You can use the cli to generate a new Job.

bash
forme make job FooBar

This will create a boilerplate file app/Jobs/FooBarJob.php for you.

Made by Moussa Clarke @ Sanders Web Works with ❤️