enqueue/async-command
Symfony Console extension to run commands asynchronously by pushing execution requests to a message queue via Enqueue. Useful for offloading long-running tasks and integrating CLI workflows with MQ-based background processing.
Installation:
composer require enqueue/async-command
Ensure you have a message broker (e.g., RabbitMQ, Redis) configured with the enqueue/enqueue package.
Configure the Queue Connection:
Add the queue connection to your config/packages/enqueue.yaml (or equivalent):
enqueue:
clients:
default:
dsn: '%env(MESSAGE_BROKER_DSN)%'
Extend a Symfony Command:
Create a command that implements Enqueue\AsyncCommand\AsyncCommandInterface:
use Enqueue\AsyncCommand\AsyncCommandInterface;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class MyAsyncCommand extends Command implements AsyncCommandInterface
{
protected function execute(InputInterface $input, OutputInterface $output): int
{
// Your command logic here
$output->writeln('Running async!');
return Command::SUCCESS;
}
}
Register the Command:
Add the command to your services.yaml:
services:
App\Command\MyAsyncCommand:
tags: ['console.command']
Run the Command Async:
Use the async:run command to dispatch the job:
php bin/console async:run App\Command\MyAsyncCommand
Trigger a long-running task (e.g., generating reports) without blocking the CLI:
php bin/console async:run App\Command\GenerateReportCommand
Check the queue worker logs to confirm execution.
Dispatching:
Use async:run to enqueue the command:
php bin/console async:run App\Command\MyCommand --option=value
Pass arguments/options as usual (they’re serialized and sent to the worker).
Worker Setup:
Start a worker process (e.g., via supervisord or systemd) to process jobs:
php bin/console enqueue:consume -vv
Handling Output:
Redirect command output to a file or log it via OutputInterface:
$output->writeln('Async output: ' . $this->getOutputFile());
Error Handling:
Implement AsyncCommandInterface::onError() to handle failures:
public function onError(InputInterface $input, OutputInterface $output, \Throwable $error)
{
$output->writeln('Error: ' . $error->getMessage());
// Send notification (e.g., email, Slack)
}
Laravel Compatibility:
Use the Symfony Console component via symfony/console and wrap commands in a Laravel service provider.
Example:
use Enqueue\AsyncCommand\AsyncCommandInterface;
use Symfony\Component\Console\Application;
class AsyncCommandServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->singleton('async.command.app', function () {
$app = new Application();
$app->add(new MyAsyncCommand());
return $app;
});
}
}
Dynamic Command Dispatch: Dispatch commands dynamically via code:
use Enqueue\AsyncCommand\AsyncCommandDispatcher;
$dispatcher = $this->container->get(AsyncCommandDispatcher::class);
$dispatcher->dispatch(new MyAsyncCommand(), ['--option' => 'value']);
Queue Prioritization:
Use queue names to prioritize jobs (e.g., high, low):
# config/packages/enqueue.yaml
enqueue:
clients:
default:
dsn: '%env(MESSAGE_BROKER_DSN)%'
queues:
high: 'high_priority'
low: 'low_priority'
Dispatch to a specific queue:
php bin/console async:run App\Command\MyCommand --queue=high
Serialization Issues:
__serialize()/__unserialize() for custom objects.Worker Crashes:
try-catch and log errors:
try {
$command->run($input, $output);
} catch (\Throwable $e) {
$output->writeln('Worker error: ' . $e->getMessage());
}
Stale Connections:
heartbeat in the broker config.Missing Dependencies:
onError().Check Queue Backlog: Use the broker’s management UI (e.g., RabbitMQ Admin) or CLI tools to inspect pending jobs:
php bin/console enqueue:list-jobs
Enable Verbose Logging:
Run the worker with -vv to see detailed logs:
php bin/console enqueue:consume -vv
Test Locally: Use Redis for development (faster than RabbitMQ):
# config/packages/enqueue.yaml
enqueue:
clients:
default:
dsn: 'redis://localhost'
Custom Serialization:
Override AsyncCommandInterface::serialize()/deserialize() for complex payloads:
public function serialize(): string
{
return json_encode(['data' => $this->getData()]);
}
public static function deserialize(string $data): self
{
return new static(json_decode($data, true)['data']);
}
Pre/Post Hooks: Extend the worker to run logic before/after command execution:
use Enqueue\AsyncCommand\AsyncCommandWorker;
class CustomAsyncCommandWorker extends AsyncCommandWorker
{
protected function beforeExecute(AsyncCommandInterface $command)
{
// Pre-execution logic (e.g., validate dependencies)
}
protected function afterExecute(AsyncCommandInterface $command, int $exitCode)
{
// Post-execution logic (e.g., cleanup)
}
}
Event Dispatching:
Trigger events (e.g., Symfony’s kernel.event_dispatcher) inside the command to decouple logic:
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
class MyAsyncCommand implements AsyncCommandInterface
{
public function __construct(private EventDispatcherInterface $dispatcher) {}
public function execute(InputInterface $input, OutputInterface $output): int
{
$this->dispatcher->dispatch(new CommandStartedEvent());
// ...
}
}
Queue Name Overrides: Override the default queue name via command option:
php bin/console async:run App\Command\MyCommand --queue=custom_queue
Environment-Specific Brokers:
Use %env(MESSAGE_BROKER_DSN)% in enqueue.yaml to switch brokers per environment (e.g., Redis for dev, RabbitMQ for prod).
Worker Concurrency: Limit worker concurrency to avoid resource exhaustion:
php bin/console enqueue:consume -vv --concurrency=5
How can I help you explore Laravel packages today?