Installation
composer require becklyn/cron-job-bundle
Add to config/bundles.php:
return [
// ...
Becklyn\CronJobBundle\CronJobBundle::class => ['all' => true],
];
First Job Definition
Create a job class in src/Command/ (or any directory):
namespace App\Command;
use Becklyn\CronJobBundle\Job\JobInterface;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class MyFirstJob extends Command implements JobInterface
{
protected static $defaultName = 'app:my-first-job';
public function run(InputInterface $input, OutputInterface $output): int
{
$output->writeln('Running my first cron job!');
return Command::SUCCESS;
}
public function getSchedule(): string
{
return '* * * * *'; // Every minute
}
}
Enable the Job
Add to config/packages/becklyn_cron_job.yaml:
jobs:
app.my_first_job: ~
Run the Scheduler
php bin/console becklyn:cron-job:run
(For production, set up a system cron to run this command periodically.)
Define a job to log system events nightly:
class NightlySystemLogJob extends Command implements JobInterface
{
public function run(InputInterface $input, OutputInterface $output): int
{
$logger = $this->getLogger();
$logger->info('Nightly system log entry', [
'timestamp' => (new \DateTime())->format('Y-m-d H:i:s'),
'event' => 'system_health_check'
]);
return Command::SUCCESS;
}
public function getSchedule(): string
{
return '0 3 * * *'; // 3 AM daily
}
}
src/
├── Command/
│ ├── User/
│ │ ├── SyncUserDataJob.php
│ │ └── CleanupInactiveUsersJob.php
│ ├── System/
│ │ └── MaintenanceLogJob.php
│ └── ...
trait LoggableJob
{
protected function logExecution(string $message): void
{
$this->getLogger()->info($message, ['job' => static::class]);
}
}
use Symfony\Component\DependencyInjection\Attribute\AutoconfigureTag;
#[AutoconfigureTag('cron_job')]
class SyncUserDataJob extends Command implements JobInterface
{
public function __construct(
private UserRepository $userRepository,
private MailerInterface $mailer
) {}
public function run(InputInterface $input, OutputInterface $output): int
{
$users = $this->userRepository->findPendingSync();
foreach ($users as $user) {
$this->mailer->sendSyncNotification($user);
}
return Command::SUCCESS;
}
}
# .env
CRON_JOB_SYNC_USERS_SCHEDULE="0 0 * * 1" # Every Monday at midnight
public function getSchedule(): string
{
return $_ENV['CRON_JOB_SYNC_USERS_SCHEDULE'] ?? '* * * * *';
}
public function run(InputInterface $input, OutputInterface $output): int
{
$this->runJob(new BackupDatabaseJob());
if ($this->shouldSendReport()) {
$this->runJob(new SendWeeklyReportJob());
}
return Command::SUCCESS;
}
private function runJob(JobInterface $job): void
{
$job->run(new ArrayInput([]), new NullOutput());
}
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
public function __construct(private EventDispatcherInterface $dispatcher) {}
public function run(InputInterface $input, OutputInterface $output): int
{
$this->dispatcher->dispatch(new JobExecutedEvent(static::class));
return Command::SUCCESS;
}
# config/packages/becklyn_cron_job.yaml
jobs:
app.user_sync:
schedule: "0 2 * * *" # Daily at 2 AM
enabled: true
timeout: 3600 # 1 hour
app.cleanup_temp_files:
schedule: "0 1 * * *" # Daily at 1 AM
enabled: "%env(bool:CRON_JOB_CLEANUP_ENABLED)%"
Schedule Parsing Errors
Cron\CronExpression:
use Cron\CronExpression;
public function getSchedule(): string
{
$schedule = '* * * * *';
if (!CronExpression::isValid($schedule)) {
throw new \RuntimeException("Invalid cron schedule: {$schedule}");
}
return $schedule;
}
Job Registration Order
cron_job:
#[AutoconfigureTag('cron_job')]
class MyJob extends Command implements JobInterface {}
Or register manually in services.yaml:
services:
App\Command\MyJob:
tags: ['cron_job']
Output Handling
OutputInterface to log progress.$output->writeln('Processing...');
$output->to($this->getOutputFile());
Long-Running Jobs
setTimeout() in Symfony 5.3+ or run heavy tasks asynchronously:
$this->getContainer()->get('messenger')->dispatch(
new ProcessHeavyTaskMessage($data)
);
Time Zone Issues
use Symfony\Component\DependencyInjection\Attribute\AutoconfigureTag;
#[AutoconfigureTag('cron_job')]
class TimeZoneAwareJob extends Command implements JobInterface
{
public function __construct(private \DateTimeZone $timeZone) {}
public function getSchedule(): string
{
return '@daily'; // Uses the configured time zone
}
}
Dry Run Mode Enable logging to preview jobs:
# config/packages/becklyn_cron_job.yaml
debug: true
Outputs scheduled jobs to var/log/dev.log.
Manual Job Execution Test jobs without waiting for the cron:
php bin/console app:my_job
Job Execution Logs Use Symfony’s logger to track runs:
public function run(InputInterface $input, OutputInterface $output): int
{
$this->logger->info('Job started', ['job' => static::class]);
try {
// Job logic
$this->logger->info('Job completed successfully');
} catch (\Throwable $e) {
$this->logger->error('Job failed', ['exception' => $e->getMessage()]);
throw $e;
}
return Command::SUCCESS;
}
use Becklyn\CronJobBundle\Repository\JobRepositoryInterface;
class DatabaseJobRepository implements JobRepositoryInterface
{
public function findAll(): array
{
return $this->entityManager->getRepository(Job::class)->findAll();
}
}
Bind it in services.yaml:
How can I help you explore Laravel packages today?