cakephp/queue
A CakePHP queue plugin for running background jobs with job classes, CLI workers, and configurable queue backends. Offload time-consuming tasks, process work asynchronously, and keep web requests fast and responsive.
cakephp/queue is a thin, queue-interop-compatible wrapper: it gives you a QueueManager, a Queue facade over an enqueue transport, a Processor that turns messages into job objects, and CakePHP console glue. It is not a Laravel package — the ergonomics (Configure::read('Queue'), plugin bootstrap, bin/cake) assume CakePHP. As a Laravel developer you should treat it as "an enqueue transport with a job-dispatch convention on top," and budget a small amount of wiring.
Read in this order:
README.md — config shape and the Queue API surface.src/QueueManager.php — how configs map to DSNs and how get() caches instances.src/Queue.php — push, pushMany, consume, receive/acknowledge/reject.src/Processor.php + src/Job/JobInterface.php — job resolution rules and the run() contract.Minimal path (evaluating it, not committing):
composer require cakephp/queue enqueue/fs # fs for local; redis/amqp-lib for real
// CakePHP: config/app.php. Laravel: mirror into config/queue.php and register the manager yourself.
'Queue' => [
'default' => ['url' => env('QUEUE_URL', 'file:///tmp/queue')],
],
use Cake\Queue\QueueManager;
$queue = QueueManager::get('default');
$queue->push('App\Job\SendInvoice', ['invoiceId' => 42]);
// worker
$queue->consume();
First real use case: a fire-and-forget webhook or invoice-email job on the fs transport, confirm the JSON file appears on disk, then change only the DSN to redis://… or amqp://…. That DSN swap is the package's actual selling point.
Config as DSN, one block per queue. Keep every transport detail in the URL string and read it from env. Define default, high, low configs mapped to separate queues rather than inventing a priority field inside the payload — priority belongs in the routing key.
Job class = envelope, service = logic. The job should do nothing but unpack the message and call a plain application service. This keeps it testable without a queue and lets you reuse the service from an HTTP controller.
namespace App\Job;
use Cake\Queue\Job\JobInterface;
use Cake\Queue\Queue\Message;
class SendInvoice implements JobInterface
{
public function run(Message $message): void
{
// 2.x used run(array $data, Queue $queue); 3.x passes a Message. Check your major.
$data = $message->getParsedBody();
app(\App\Services\InvoiceMailer::class)->send($data['invoiceId']);
}
}
Use aliases for cross-environment routing. The processor resolves either a FQCN or a <config>.<alias> name registered in configuration. Aliases let staging route default.Email to a different class or queue without touching call sites.
Push from the request, consume from a supervised process. The producer path is push/pushMany inside a request (bulk import → one pushMany, not a loop of push). The consumer path is a long-running CLI process under systemd/supervisor. Do not consume inside a web request.
Laravel integration is a service provider plus one artisan command. Bind the QueueManager as a singleton, hydrate it from config/queue.php, then write a queue:work-shaped command that calls consume() with iteration/runtime caps so deploys can restart it cleanly. You are essentially rebuilding Horizon's process model minus the dashboard.
Test without consuming. Swap the DSN to enqueue/null (or fs into a temp dir) and assert on the pushed envelope, or inject a spy Queue into the producer. Integration tests that actually consume are the slowest and flakiest part of a queue suite — keep them few.
Make run() idempotent. All enqueue transports are at-least-once. Key side effects on a message id or a business key (invoiceId + status), or you will double-send on every worker crash.
cakephp/core, Configure, and Cake's console layer. In Laravel you'll be constructing objects manually and ignoring the shell commands. Budget a day, not an hour.cakephp/queue alone does nothing — you must also install enqueue/redis, enqueue/amqp-lib, enqueue/fs, etc. Version pins in the interop ecosystem are conservative; check PHP compatibility before committing.reject()/requeue() primitives. If you want DLQ behaviour you implement it: catch in the job or wrap the processor, increment an attempt counter in the payload, and reject past a threshold.maxIterations/maxRuntime consumption options and recycle the process; also restart workers on deploy or they'll run old code.fs. The transport writes human-readable JSON files you can cat. Add a --verbose flag to your worker command mirroring Cake's consumption verbosity and print the raw envelope on failure — it's faster than attaching a debugger.Cake\Queue\Consumption\* traits, but documentation is thin. Read src/Consumption/ directly; wrapping consume() in your own try/finally is usually simpler than composing traits.JobInterface signature and QueueManager config keys between majors before upgrading.How can I help you explore Laravel packages today?