Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Async Command Laravel Package

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.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require enqueue/async-command
    

    Ensure you have a message broker (e.g., RabbitMQ, Redis) configured with the enqueue/enqueue package.

  2. Configure the Queue Connection: Add the queue connection to your config/packages/enqueue.yaml (or equivalent):

    enqueue:
        clients:
            default:
                dsn: '%env(MESSAGE_BROKER_DSN)%'
    
  3. 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;
        }
    }
    
  4. Register the Command: Add the command to your services.yaml:

    services:
        App\Command\MyAsyncCommand:
            tags: ['console.command']
    
  5. Run the Command Async: Use the async:run command to dispatch the job:

    php bin/console async:run App\Command\MyAsyncCommand
    

First Use Case: Background Processing

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.


Implementation Patterns

Workflow: Async Command Execution

  1. 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).

  2. Worker Setup: Start a worker process (e.g., via supervisord or systemd) to process jobs:

    php bin/console enqueue:consume -vv
    
  3. Handling Output: Redirect command output to a file or log it via OutputInterface:

    $output->writeln('Async output: ' . $this->getOutputFile());
    
  4. 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)
    }
    

Integration Tips

  • 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
    

Gotchas and Tips

Pitfalls

  1. Serialization Issues:

    • Problem: Complex objects (e.g., closures, resources) in command arguments/options cannot be serialized.
    • Fix: Use primitive types (strings, arrays) or implement __serialize()/__unserialize() for custom objects.
  2. Worker Crashes:

    • Problem: Workers may die silently if exceptions aren’t caught.
    • Fix: Wrap worker execution in a try-catch and log errors:
      try {
          $command->run($input, $output);
      } catch (\Throwable $e) {
          $output->writeln('Worker error: ' . $e->getMessage());
      }
      
  3. Stale Connections:

    • Problem: Long-running workers may hold connections open, causing timeouts.
    • Fix: Use connection pooling or set heartbeat in the broker config.
  4. Missing Dependencies:

    • Problem: The worker may fail if required services (e.g., databases) aren’t available.
    • Fix: Implement retry logic with exponential backoff in onError().

Debugging

  1. 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
    
  2. Enable Verbose Logging: Run the worker with -vv to see detailed logs:

    php bin/console enqueue:consume -vv
    
  3. Test Locally: Use Redis for development (faster than RabbitMQ):

    # config/packages/enqueue.yaml
    enqueue:
        clients:
            default:
                dsn: 'redis://localhost'
    

Extension Points

  1. 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']);
    }
    
  2. 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)
        }
    }
    
  3. 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());
            // ...
        }
    }
    

Config Quirks

  1. Queue Name Overrides: Override the default queue name via command option:

    php bin/console async:run App\Command\MyCommand --queue=custom_queue
    
  2. Environment-Specific Brokers: Use %env(MESSAGE_BROKER_DSN)% in enqueue.yaml to switch brokers per environment (e.g., Redis for dev, RabbitMQ for prod).

  3. Worker Concurrency: Limit worker concurrency to avoid resource exhaustion:

    php bin/console enqueue:consume -vv --concurrency=5
    
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
terminal42/code-quality-tools
codifyo/ts-generator-bundle
andydefer/laravel-cluster
testo/fiber
mintobit/jobqueue
a4sex/maintenance-bundle
a4sex/entity-date-update
a4sex/client-identifier
a4sex/base-utilites
a4sex/key-value-storage
a4sex/micro-status
chilldev/dependency-injection-extra
datinglibre/datinglibre-app-api
biberltd/corebundle
bricre/symfony-bundle-test
biberltd/logbundle
dominium/http-adapter-bundle
dominium/google-analytics
a4sex/auto-clean-entity
christhompsontldr/laravel-inky