Installation:
composer require daanbiesterbos/job-queue-bundle
Register Bundle:
Add to config/bundles.php:
JMS\JobQueueBundle\JMSJobQueueBundle::class => ['all' => true],
Prepare Job Queue Console:
Copy and modify bin/console to bin/job-queue:
cp bin/console bin/job-queue
Replace use Symfony\Bundle\FrameworkBundle\Console\Application; with:
use JMS\JobQueueBundle\Console\Application;
First Job Creation:
Use the Job entity to queue a command:
use JMS\JobQueueBundle\Entity\Job;
$job = new Job();
$job->setCommand('app:your-command');
$job->setSchedule(new \DateTime('+1 hour')); // Optional: Schedule for later
$job->setData(['arg1' => 'value1']); // Optional: Command arguments
$em->persist($job);
$em->flush();
Run the Queue Worker:
php bin/job-queue run
Queue a command (e.g., app:send-emails) to run asynchronously:
$job = new Job();
$job->setCommand('app:send-emails');
$job->setData(['user_id' => 123]);
$em->persist($job);
$em->flush();
Trigger the worker in a separate terminal:
php bin/job-queue run
Job Creation:
Job entity to encapsulate command metadata (name, arguments, schedule).$job = new Job();
$job->setCommand('cache:clear');
$job->setSchedule(new \DateTime('now')); // Run immediately
$em->persist($job);
Scheduling Jobs:
$job->setSchedule(new \DateTime('tomorrow'));
JobQueueBundle's CronJob entity if extended).Job Data Serialization:
$job->setData(json_encode(['users' => [1, 2, 3], 'template' => 'welcome']));
Worker Management:
php bin/job-queue run --loop # Default (continuous)
php bin/job-queue run --once # Single execution
--concurrency=N (e.g., --concurrency=5).Symfony/Laravel Hybrid:
Process component to call bin/job-queue:
$process = new Process(['php', 'bin/job-queue', 'run', '--once']);
$process->run();
Database-Driven Jobs:
Job entity to add custom fields (e.g., priority, retries):
/**
* @ORM\Column(type="integer")
*/
private $priority = 0;
$jobs = $em->createQueryBuilder()
->select('j')
->from('JMSJobQueueBundle:Job', 'j')
->orderBy('j.priority', 'DESC')
->getQuery()
->getResult();
Event Listeners:
JobCreated, JobExecuted) to log or notify:
use JMS\JobQueueBundle\Event\JobEvent;
$dispatcher->addListener(JobEvent::JOB_CREATED, function (JobEvent $event) {
// Log job creation
});
Testing:
Job repository in tests:
$jobRepo = $this->createMock(JobRepository::class);
$jobRepo->method('findNextJob')->willReturn($job);
$this->container->set('jms_job_queue.job.repository', $jobRepo);
Worker Stuck in Loop:
--verbose).Job entities are marked as executed after completion).--once to debug.Command Arguments:
setData() will cause errors.$job->setData(['object' => json_encode($complexObject)]);
Database Locks:
locked_at column to Job:
$job->setLockedAt(new \DateTime());
$em->flush();
Timezone Issues:
Schedule uses the system timezone. Explicitly set timezone in jobs:
$schedule = new \DateTime('+1 hour', new \DateTimeZone('UTC'));
$job->setSchedule($schedule);
Worker Logs:
php bin/job-queue run --verbose
php bin/job-queue run --verbose >> /var/log/job-queue.log 2>&1
Job Inspection:
SELECT * FROM job WHERE executed_at IS NULL;
Command Execution:
php bin/console app:your-command --arg=value
bin/job-queue (same environment as bin/console).Custom Job Types:
Job entity to support custom logic:
namespace App\Entity;
use JMS\JobQueueBundle\Entity\Job as BaseJob;
class CustomJob extends BaseJob {
private $customField;
// Add getters/setters and ORM mappings
}
Worker Hooks:
namespace App\JobQueue;
use JMS\JobQueueBundle\Worker;
class CustomWorker extends Worker {
protected function executeJob(Job $job) {
// Custom logic before execution
parent::executeJob($job);
// Custom logic after execution
}
}
services.yaml:
JMS\JobQueueBundle\Worker: '@App\JobQueue\CustomWorker'
Queue Prioritization:
namespace App\Repository;
use Doctrine\ORM\EntityRepository;
class JobRepository extends EntityRepository {
public function findNextJob() {
return $this->createQueryBuilder('j')
->where('j.executed_at IS NULL')
->orderBy('j.priority', 'DESC')
->getQuery()
->getOneOrNullResult();
}
}
services.yaml to use the custom repository:
jms_job_queue.job.repository: '@App\Repository\JobRepository'
Retry Mechanism:
retries column to Job and implement exponential backoff:
if ($job->getRetries() >= 3) {
throw new \RuntimeException('Max retries exceeded');
}
$job->setRetries($job->getRetries() + 1);
$job->setNextRun(new \DateTime('+1 minute'));
Environment Variables:
JMS_JOB_QUEUE_CONCURRENCY for default concurrency..env:
JMS_JOB_QUEUE_CONCURRENCY=3
Doctrine Configuration:
Job entity is mappedHow can I help you explore Laravel packages today?