aboutcoders/job-bundle
Symfony bundle for asynchronous job processing via method annotations and DI services. Track status, cancel/update/restart jobs, run scheduled (cron) jobs, and expose a JSON REST API. Supports many queues: DBAL, RabbitMQ, Redis, SQS, IronMQ, Beanstalkd, more.
Installation
composer require aboutcoders/job-bundle
Add to AppKernel.php (Symfony 2.x) or config/bundles.php (Symfony 3/4):
new Aboutcoders\JobBundle\AbcJobBundle(),
Configure Queue Driver
Edit app/config/config.yml (or config/packages/aboutcoders_job.yaml in Symfony 4+):
aboutcoders_job:
queue_driver: doctrine_dbal # or rabbitmq, redis, etc.
Annotate a Job Method
use Aboutcoders\JobBundle\Annotation\Job;
class MyJobService
{
/**
* @Job()
*/
public function processData($data)
{
// Your async logic here
}
}
Register the Service
Tag the service in services.yml:
services:
my_job_service:
class: AppBundle\Service\MyJobService
tags:
- { name: aboutcoders_job.job }
Trigger a Job
$this->get('aboutcoders_job.job_dispatcher')->dispatch(
'my_job_service',
'processData',
['data' => 'example']
);
Replace a slow API endpoint with an async job:
/**
* @Job()
*/
public function generateReport($userId)
{
$report = $this->reportService->generate($userId);
$this->emailService->send($report);
}
Call from controller:
$dispatcher = $this->get('aboutcoders_job.job_dispatcher');
$dispatcher->dispatch('report_service', 'generateReport', ['userId' => 123]);
$dispatcher->dispatch('service_name', 'method_name', $params);
$dispatcher->dispatch('service_name', 'method_name', $params, 10); // Higher = sooner
$dispatcher->dispatch('service_name', 'method_name', $params, null, 3600); // Delay in seconds
$status = $this->get('aboutcoders_job.job_manager')->getJobStatus($jobId);
$this->get('aboutcoders_job.job_manager')->cancelJob($jobId);
$this->get('aboutcoders_job.job_manager')->retryJob($jobId);
Configure in config.yml:
aboutcoders_job:
scheduled_jobs:
'report_service.generateMonthlyReport':
cron: '0 0 1 * *' # Run at 00:00 on the 1st day of every month
parameters: ['all']
Expose job endpoints in routing.yml:
aboutcoders_job_rest:
resource: "@AbcJobBundle/Resources/config/routing.yml"
prefix: /api/jobs
Access endpoints like:
GET /api/jobs/{id} → Job statusPOST /api/jobs/{id}/cancel → Cancel jobAnnotate entity methods:
use Aboutcoders\JobBundle\Annotation\Job;
class UserService
{
/**
* @Job()
*/
public function notifyUser(User $user, $message)
{
$user->addNotification($message);
$this->entityManager->flush();
}
}
Trigger jobs after form submission:
public function submitAction(Request $request)
{
$form = $this->createForm(UserType::class);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$user = $form->getData();
$this->get('aboutcoders_job.job_dispatcher')->dispatch(
'user_service',
'notifyUser',
[$user, 'Your data was saved!']
);
$this->addFlash('success', 'Processing in background!');
}
}
Listen for job lifecycle events:
services:
app.job_listener:
class: AppBundle\EventListener\JobListener
tags:
- { name: kernel.event_listener, event: aboutcoders.job.created, method: onJobCreated }
Extend Aboutcoders\JobBundle\Queue\QueueInterface for new drivers:
class CustomQueueDriver implements QueueInterface
{
public function push($jobId, $data, $priority = null, $delay = null)
{
// Custom logic (e.g., HTTP API call)
}
public function pop()
{
// Custom logic
}
}
Register in config:
aboutcoders_job:
queue_driver: custom
queue_driver_options:
class: AppBundle\Queue\CustomQueueDriver
Serialization Issues
@Job(serializer="json") or implement JsonSerializable:
/**
* @Job(serializer="json")
*/
public function processEntity(Entity $entity)
{
// ...
}
Queue Driver Mismatches
doctrine_dbal without DBAL installed or misconfigured.queue_driver_options are correct:
aboutcoders_job:
queue_driver: doctrine_dbal
queue_driver_options:
db_driver: pdo_mysql
db_host: localhost
db_name: your_db
Circular Dependencies
@Job(allow_recursion=true) or refactor to avoid circular calls.Stale Job Status
aboutcoders_job.job_manager->updateJobStatus().Memory Leaks
@Job(timeout=30) to set max execution time (in seconds).Enable Logging
Configure in config.yml:
aboutcoders_job:
logging:
enabled: true
level: debug
handlers:
- { type: stream, path: "%kernel.logs_dir%/jobs.log" }
Check Queue Backlog
For doctrine_dbal:
SELECT * FROM aboutcoders_job_queue;
For RabbitMQ:
rabbitmqctl list_queues name messages
Worker Debugging Run worker with verbose output:
php app/console aboutcoders:job:consume --verbose
Job Retry Logic
Configure retry policy in config.yml:
aboutcoders_job:
retry_policy:
max_attempts: 3
delay: 1000 # ms between retries
Custom Job Statuses
Extend Aboutcoders\JobBundle\Model\JobStatus:
class CustomJobStatus extends JobStatus
{
const STATUS_PROCESSING = 'processing_custom';
}
Update JobManager to handle new statuses.
Pre/Post Job Hooks
Implement Aboutcoders\JobBundle\Event\JobEventSubscriber:
class MyJobSubscriber implements JobEventSubscriber
{
public function onJobCreated(JobEvent $event)
{
// Pre-job logic
}
public function onJobFinished(JobEvent $event)
{
// Post-job logic
}
}
Register in services.yml:
services:
app.job_subscriber:
class: AppBundle\EventListener\MyJobSubscriber
tags:
- { name: aboutcoders_job.event_subscriber }
Dynamic Job Parameters
Use runtime parameters with @Job:
/**
* @Job(runtime_parameters={"userId": "@user_id"})
*/
public function sendWelcomeEmail($userId)
{
// ...
}
Dispatch with:
$dispatch
How can I help you explore Laravel packages today?