How to cancel queued job in Laravel or Redis

Comprehensive Answer:

I now use my own custom DispatchableWithControl trait instead of the Dispatchable trait.

I call it like this:

$executeAt = Carbon::now()->addDays(7)->addHours(2)->addMinutes(17);
SomeJobThatWillSendAnEmailOrDoWhatever::dispatch($contactId, $executeAt);

namespace App\Jobs;

use App\Models\Tag;
use Carbon\Carbon;
use Exception;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use Log;

class SomeJobThatWillSendAnEmailOrDoWhatever implements ShouldQueue {

    use DispatchableWithControl,
        InteractsWithQueue,
        Queueable,
        SerializesModels;

    protected $contactId;
    protected $executeAt;

    /**
     * 
     * @param string $contactId
     * @param Carbon $executeAt
     * @return void
     */
    public function __construct($contactId, $executeAt) {
        $this->contactId = $contactId;
        $this->executeAt = $executeAt;
    }

    /**
     * Execute the job. 
     *
     * @return void
     */
    public function handle() {
        if ($this->checkWhetherShouldExecute($this->contactId, $this->executeAt)) {
            //do stuff here
        }
    }

    /**
     * The job failed to process. 
     *
     * @param  Exception  $exception
     * @return void
     */
    public function failed(Exception $exception) {
        // Send user notification of failure, etc...
        Log::error(static::class . ' failed: ' . $exception);
    }

}

namespace App\Jobs;

use App\Models\Automation;
use Carbon\Carbon;
use Illuminate\Foundation\Bus\PendingDispatch;
use Log;

trait DispatchableWithControl {

    use \Illuminate\Foundation\Bus\Dispatchable {//https://stackoverflow.com/questions/40299080/is-there-a-way-to-extend-trait-in-php
        \Illuminate\Foundation\Bus\Dispatchable::dispatch as parentDispatch;
    }

    /**
     * Dispatch the job with the given arguments.
     *
     * @return \Illuminate\Foundation\Bus\PendingDispatch
     */
    public static function dispatch() {
        $args = func_get_args();
        if (count($args) < 2) {
            $args[] = Carbon::now(TT::UTC); //if $executeAt wasn't provided, use 'now' (no delay)
        }
        list($contactId, $executeAt) = $args;
        $newAutomationArray = [
            'contact_id' => $contactId,
            'job_class_name' => static::class,
            'execute_at' => $executeAt->format(TT::MYSQL_DATETIME_FORMAT)
        ];
        Log::debug(json_encode($newAutomationArray));
        Automation::create($newAutomationArray);
        $pendingDispatch = new PendingDispatch(new static(...$args));
        return $pendingDispatch->delay($executeAt);
    }

    /**
     * @param int $contactId
     * @param Carbon $executeAt
     * @return boolean
     */
    public function checkWhetherShouldExecute($contactId, $executeAt) {
        $conditionsToMatch = [
            'contact_id' => $contactId,
            'job_class_name' => static::class,
            'execute_at' => $executeAt->format(TT::MYSQL_DATETIME_FORMAT)
        ];
        Log::debug('checkWhetherShouldExecute ' . json_encode($conditionsToMatch));
        $automation = Automation::where($conditionsToMatch)->first();
        if ($automation) {
            $automation->delete();
            Log::debug('checkWhetherShouldExecute = true, so soft-deleted record.');
            return true;
        } else {
            return false;
        }
    }

}

So, now I can look in my 'automations' table to see pending jobs, and I can delete (or soft-delete) any of those records if I want to prevent the job from executing.


Make it easier.

Don't send an email with the later option. You must dispatch a Job with the later option, and this job will be responsible to send the email.

Inside this job, before send the email, check the emailAddress-sendTime pair. If is correct, send the email, if not, return true and the email won't send and the job will finish.


Maybe instead of canceling it you can actually remove it from the Redis, from what Ive read from official docs about forget command on Redis and from Laravel official doc interacting with redis you can basically call any Redis command from the interface, if you could call the forget command and actually pass node_id which in this case I think it's that number you have in your image DEL 1517797158 I think you could achieve the "cancel".