draw/cron-job
Manage cron jobs stored in the database: queue due jobs and execute them via Symfony Messenger workers. Includes console commands to enqueue due jobs or run by name, optional Sonata Admin pages, and Doctrine ORM mapping configuration.
Installation:
composer require draw/cron-job
Ensure symfony/messenger and doctrine/orm are installed (or use Laravel’s Doctrine bridge if needed).
Configuration:
Add to config/packages/draw_framework_extra.yaml (or Laravel’s equivalent):
draw_framework_extra:
cron_job:
enabled: true
doctrine:
orm:
mappings:
DrawCronJob:
is_bundle: false
type: attribute
dir: "%kernel.project_dir%/vendor/draw/cron-job/src/Entity"
prefix: "Draw\Component\CronJob\Entity"
Messenger Setup:
Configure config/packages/messenger.yaml (or Laravel’s config/messenger.php):
framework:
messenger:
transports:
async: "%env(MESSENGER_TRANSPORT_DSN)%" # e.g., 'doctrine://default'
routing:
Draw\Component\CronJob\Message\ExecuteCronJobMessage: async
Database Migrations:
Run Doctrine migrations to create cron_job and cron_job_execution tables:
php bin/console doctrine:migrations:diff
php bin/console doctrine:migrations:migrate
First Cron Job: Create a cron job via the admin panel (Sonata) or manually:
use Draw\Component\CronJob\Entity\CronJob;
$cronJob = new CronJob();
$cronJob->setName('send-nightly-reports');
$cronJob->setSchedule('0 0 * * *'); // Daily at midnight
$cronJob->setCommand('app:send-reports'); // Your Laravel command
$entityManager->persist($cronJob);
$entityManager->flush();
Trigger Due Jobs:
Run the command to process overdue jobs (schedule this via cron):
php bin/console draw:cron-job:queue-due
Manual Execution: Test a job manually:
php bin/console draw:cron-job:queue-by-name send-nightly-reports
CronJob) with fields like name, schedule (cron expression), command, enabled, and last_run_at.// src/Entity/CronJob.php (extend Draw\Component\CronJob\Entity\CronJob)
class AppCronJob extends CronJob {
// Custom fields or logic
}
ExecuteCronJobMessage and processed asynchronously.CronJobProcessor to dispatch to Laravel’s queue:
// app/Services/CustomCronJobProcessor.php
use Illuminate\Support\Facades\Bus;
class CustomCronJobProcessor implements CronJobProcessorInterface {
public function process(CronJob $cronJob) {
Bus::dispatch(new HandleCronJob($cronJob));
}
}
transports:
async: 'doctrine://default' # or 'redis://localhost'
app:send-reports) in CronJob::setCommand().ExecuteCronJobMessageHandler to add pre/post-execution logic:
// app/MessageHandlers/CustomCronJobHandler.php
use Draw\Component\CronJob\Message\ExecuteCronJobMessage;
class CustomCronJobHandler implements MessageHandlerInterface {
public function __invoke(ExecuteCronJobMessage $message) {
// Pre-execution logic
$exitCode = shell_exec($message->getCommand());
// Post-execution logic
return $exitCode;
}
}
CronJobExecution records for every run, including:
started_at, ended_at, durationexit_code, output, error_output$executions = $entityManager->getRepository(CronJobExecution::class)
->findBy(['cronJob' => $cronJob]);
queue-due command in your server’s cron:
* * * * * php /path/to/your/project/bin/console draw:cron-job:queue-due
// app/Console/Kernel.php
protected function schedule(Schedule $schedule) {
$schedule->command('draw:cron-job:queue-due')->everyMinute();
}
Doctrine + Eloquent:
// app/Repositories/CronJobRepository.php
class CronJobRepository {
public function findByName(string $name) {
return CronJob::query()->where('name', $name)->first();
}
}
Service Container:
config/services.php:
'Draw\Component\CronJob\CronJobProcessor' => \Draw\Component\CronJob\CronJobProcessor::class,
Artisan Commands:
app/Console/Kernel.php:
protected $commands = [
\Draw\Component\CronJob\Command\QueueDueCronJobsCommand::class,
\Draw\Component\CronJob\Command\QueueCronJobByNameCommand::class,
];
Job Dependencies:
depends_on field in CronJob to chain jobs (e.g., job_b runs after job_a).CronJobProcessor:
$dependentJobs = $entityManager->getRepository(CronJob::class)
->findBy(['depends_on' => $cronJob->getName()]);
Dynamic Scheduling:
CronJob::isDue() to add dynamic conditions (e.g., run only on weekdays):
public function isDue(): bool {
return parent::isDue() && now()->dayOfWeek !== \Carbon\Carbon::SUNDAY;
}
Event Dispatching:
CronJobExecuted events to trigger notifications or analytics:
// app/Listeners/CronJobExecutedListener.php
use Draw\Component\CronJob\Event\CronJobExecuted;
class CronJobExecutedListener {
public function handle(CronJobExecuted $event) {
Log::info("Cron job {$event->getCronJob()->getName()} executed");
}
}
Rate Limiting:
max_runs_per_hour field to CronJob and validate in CronJobProcessor:
$lastRuns = $entityManager->getRepository(CronJobExecution::class)
->count(['cronJob' => $cronJob, 'createdAt' => now()->subHour()]);
if ($lastRuns >= $cronJob->getMaxRunsPerHour()) {
throw new \RuntimeException('Rate limit exceeded');
}
Symfony-Laravel Conflicts:
transports:
async: 'doctrine://default' # Avoids Redis conflicts
Doctrine vs. Eloquent:
php bin/console doctrine:migrations:execute --query="CREATE TABLE IF NOT EXISTS cron_job (...)"
**C
How can I help you explore Laravel packages today?