aboutcoders/scheduler-bundle
Symfony bundle for defining recurring schedules and dispatching notifications via the Symfony EventDispatcher. Designed to be integrated into your app by implementing your own schedule entities, with docs for installation, configuration, and custom schedule types.
Installation:
composer require aboutcoders/scheduler-bundle
Enable the bundle in config/bundles.php:
Aboutcoders\SchedulerBundle\AboutcodersSchedulerBundle::class => ['all' => true],
First Use Case:
Define a schedule entity (e.g., App\Entity\MySchedule) extending Aboutcoders\SchedulerBundle\Entity\AbstractSchedule:
use Aboutcoders\SchedulerBundle\Entity\AbstractSchedule;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity]
class MySchedule extends AbstractSchedule
{
// Custom fields (e.g., cron expression, event name)
#[ORM\Column(type: 'string')]
private string $eventName;
// Getters/setters...
}
Dispatch Events: Configure a listener to trigger actions when schedules run:
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Aboutcoders\SchedulerBundle\Event\ScheduleEvent;
class MyScheduleSubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents(): array
{
return [
'my.schedule.event' => 'onScheduleRun',
];
}
public function onScheduleRun(ScheduleEvent $event)
{
// Handle logic (e.g., send email, update DB)
}
}
Run the Scheduler: Add a cron job to execute the scheduler command:
php bin/console aboutcoders:scheduler:run
Schedule it (e.g., every minute):
* * * * * /path/to/php bin/console aboutcoders:scheduler:run >> /dev/null 2>&1
Schedule Definition:
AbstractSchedule for custom schedules.@Cron) or fields (e.g., cronExpression) to define recurrence.#[ORM\Entity]
class DailyReportSchedule extends AbstractSchedule
{
#[ORM\Column(type: 'string')]
private string $cronExpression = '0 0 * * *'; // Daily at midnight
}
Event-Driven Actions:
ScheduleEvent listeners for side effects (e.g., notifications, API calls).public function onScheduleRun(ScheduleEvent $event)
{
$schedule = $event->getSchedule();
$this->mailer->send(new Email($schedule->getEventName()));
}
Integration with Jobs:
aboutcoders/job-bundle to queue delayed tasks:
use Aboutcoders\JobBundle\Job\JobInterface;
class SendReportJob implements JobInterface
{
public function run(): void
{
// Send report logic
}
}
Dynamic Scheduling:
ScheduleManager to create/update schedules programmatically:
$schedule = $this->scheduleManager->create(
MySchedule::class,
['eventName' => 'user.activation']
);
$schedule->setCronExpression('0 9 * * *'); // 9 AM daily
$this->entityManager->persist($schedule);
*/5 * * * * for every 5 minutes) to balance precision and load.$this->logger->info('Schedule run', ['schedule' => $schedule->getId()]);
Cron Syntax Errors:
0 25 * * * for 25:00) will silently fail. Validate with:
use Cron\CronExpression;
$expr = CronExpression::factory($schedule->getCronExpression());
Missing Doctrine Entities:
NoSuchEntityException.php bin/console doctrine:schema:validate
Event Dispatcher Misconfiguration:
eventName field in the schedule entity. Typos here will make schedules appear "invisible."php bin/console debug:event-dispatcher
Time Zone Issues:
$schedule->setTimezone(new \DateTimeZone('America/New_York'));
schedules table for last_run_at and next_run_at to verify timing.debug: true in config/packages/aboutcoders_scheduler.yaml to log skipped schedules.php bin/console aboutcoders:scheduler:run --dry-run
Custom Schedule Types:
Aboutcoders\SchedulerBundle\Schedule\ScheduleInterface for non-cron schedules (e.g., "run every 3 hours").services:
App\Schedule\CustomSchedule:
tags:
- { name: aboutcoders_scheduler.schedule_type, type: custom }
Pre/Post Hooks:
AbstractSchedule to add lifecycle methods (e.g., onBeforeRun(), onAfterRun()).Conditional Execution:
if (!$this->isValidDay($event->getSchedule()->getCreatedAt())) {
return;
}
cronExpression is empty, the schedule won’t run. Always set a default.symfony/lock) if needed:
use Symfony\Component\Lock\LockFactory;
$lock = $this->lockFactory->createLock('my_schedule_lock', 300);
if (!$lock->acquire()) {
return; // Skip if another run is in progress
}
How can I help you explore Laravel packages today?