Queue Service Provider
Queue bus, transports, handlers and worker infrastructure.
Responsibility
Registers QueueBusInterface and alias queue for message dispatching. QueueServiceProvider builds transports from queue.transports, uses queue.default as the default transport and registers handlers from queue.handlers. The database transport depends on the configured database driver, serializer and queue table names.
Queue config
Application queue configuration lives in app/Config/Queue.php. sync handles messages immediately; database stores them for worker processing.
<?php
declare(strict_types=1);
use App\Queue\DemoMessage;
use App\Queue\DemoMessageHandler;
return [
'default' => 'sync',
'transports' => ['sync', 'database'],
'database' => [
'table' => 'system_queue_job',
'failed_table' => 'system_queue_failed_job',
],
'handlers' => [
DemoMessage::class => DemoMessageHandler::class,
],
];
Message DTO
Messages are plain objects. Keep them serializable and explicit; database transport serializes the message payload before storing it.
namespace App\Queue;
final class DemoMessage
{
public function __construct(
private readonly string $message,
) {}
public function message(): string
{
return $this->message;
}
}
Handler
A handler can be any callable. Class-string handlers are resolved through the container, so handler classes can use constructor injection when registered as services or autowired by the container.
namespace App\Queue;
final class DemoMessageHandler
{
public function __invoke(DemoMessage $message): void
{
// Handle queued message here.
}
}
Dispatching
Prefer constructor injection in services/controllers. Without an explicit transport, dispatch uses queue.default.
use App\Queue\DemoMessage;
use Lemonade\Framework\Queue\QueueBusInterface;
final class ExampleService
{
public function __construct(
private readonly QueueBusInterface $queue,
) {}
public function run(): void
{
$this->queue->dispatch(new DemoMessage('Example message'));
}
}
Database dispatch
Pass transport: database when the message should be persisted and handled by a worker instead of the current request/process.
$this->queue->dispatch(
message: new DemoMessage('Example message'),
transport: 'database',
queue: 'default',
);
CLI worker
Database transport needs queue tables and a worker. queue:install creates tables from queue.database.table and queue.database.failed_table. queue:work default database 1 processes one job from queue default using transport database; when the queue is empty, the worker waits and prints a waiting line so it is clear the process is running.
vendor/bin/lemonade queue:install
vendor/bin/lemonade queue:work default database 1
Sync vs database
sync transport invokes the handler immediately in the same request/process. database transport stores the message and a worker processes it later. queue:work rejects sync, because sync transport has no persisted queue to poll.
SQLite skeleton note
The skeleton uses SQLite for a local database queue example. Enable pdo_sqlite and sqlite3 in both CLI PHP and web PHP configuration. Configure SQLite through DB_DRIVER=pdo, DB_DIALECT=sqlite and DB_DSN=sqlite:storage/skeleton.sqlite; DB_DRIVER=sqlite is not a valid framework driver.
Schema install
queue:install uses the framework schema layer (Schema + TableBlueprint) and grammar-specific compilation. MySQL keeps indexes inline in CREATE TABLE; SQLite creates plain indexes after the table with CREATE INDEX IF NOT EXISTS, so repeated installs are safe.