symfony/scheduler
Symfony Scheduler component adds cron-like task scheduling powered by Symfony Messenger. Define recurring or one-off jobs, route them through transports and workers, and run tasks reliably within Symfony apps with first-class framework integration.
Installation:
composer require symfony/scheduler
(Note: In Laravel, this is redundant—use native schedule:run instead.)
Define a Scheduled Task (Symfony-style):
use Symfony\Component\Scheduler\Attribute\AsCronTask;
#[AsCronTask("0 0 * * *", name: "daily_report")]
public function generateDailyReport(): void
{
// Task logic
}
(Laravel equivalent: Schedule::command('report:generate')->daily();)
Register the Task as a Service:
# config/services.yaml (Symfony)
services:
App\Command\GenerateDailyReportCommand:
tags: [scheduler.task]
(Laravel: No config needed—use Schedule::command() in app/Console/Kernel.php.)
Run the Scheduler:
php bin/console scheduler:consume
(Laravel: php artisan schedule:run via cron or schedule:work for testing.)
Symfony Scheduler:
#[AsCronTask("*/5 * * * *", name: "sync_data")]
public function syncData(): void
{
// Sync logic
}
Laravel Equivalent:
protected function schedule(Schedule $schedule): void
{
$schedule->command('sync:data')->everyFiveMinutes();
}
Key Difference: Symfony requires #[AsCronTask] attributes + Messenger setup, while Laravel uses fluent methods in Kernel.php.
Symfony:
#[AsFrequencyTask("PT1H", name: "hourly_backup")]
public function backupDatabase(): void
{
// Backup logic
}
Laravel:
$schedule->command('backup:run')->hourly();
Pattern: Use attributes in Symfony; use method chaining in Laravel. Symfony requires explicit scheduler.task tag registration.
ScheduleProviderInterfaceSymfony:
use Symfony\Component\Scheduler\ScheduleProviderInterface;
class DynamicScheduleProvider implements ScheduleProviderInterface
{
public function getSchedules(): iterable
{
yield new CronTask(
'0 0 * * *',
new Message(new GenerateReportCommand())
);
}
}
Laravel Equivalent:
$schedule->call([ReportService::class, 'generate'])->dailyAt('00:00');
Pattern: Symfony’s ScheduleProviderInterface is useful for runtime-generated schedules. Laravel achieves this via closures or dependency injection in Schedule::call().
Symfony:
# config/packages/messenger.yaml
framework:
messenger:
transports:
async: '%env(MESSENGER_TRANSPORT_DSN)%'
routing:
'Symfony\Component\Scheduler\Message\ScheduleMessage': async
Laravel:
// config/queue.php
'connections' => [
'database' => [
'driver' => 'database',
'table' => 'jobs',
'queue' => 'default',
],
],
Pattern: Symfony ties scheduling to Messenger transports (e.g., AMQP, Doctrine, Redis). Laravel uses its own queue system—no bridging needed.
Symfony:
use Symfony\Component\Scheduler\Event\PostRunEvent;
public function onTaskRun(PostRunEvent $event): void
{
if ($event->isSuccess()) {
// Log success
}
}
Laravel:
use Illuminate\Bus\FailedJob;
use Illuminate\Queue\Events\JobProcessed;
Event::listen(JobProcessed::class, function (JobProcessed $event) {
if (!$event->job->failed()) {
// Log success
}
});
Pattern: Symfony’s PostRunEvent mirrors Laravel’s JobProcessed/FailedJob events. Symfony requires manual event listeners; Laravel uses built-in queue events.
Symfony:
#[AsCronTask("*/10 * * * *", name: "log_metrics", skipIfMissed: true)]
public function logMetrics(): void
{
// Metrics logic
}
Laravel:
$schedule->command('metrics:log')->everyTenMinutes()->withoutOverlapping();
Pattern: Symfony’s skipIfMissed is similar to Laravel’s withoutOverlapping(), but Symfony requires attribute configuration.
PHP Version Mismatch:
Duplicate Schedule Names:
name in #[AsCronTask].scheduler.task service tags in DI container.Schedule uses command/job namespaces to avoid collisions.Messenger Transport Complexity:
scheduler:consume requires a running Messenger worker (e.g., php bin/console messenger:consume async).php artisan queue:work—no extra setup needed.Attribute vs. Method Chaining:
#[AsCronTask] attributes, which may feel verbose for simple schedules.#[AsFrequencyTask] for interval-based tasks (e.g., PT1H for hourly).->hourly()) is more concise.No Native Laravel Integrations:
scheduler:list command for basic visibility.Check Schedule Registration:
php bin/console debug:container App\Command\MyTaskCommand
scheduler.task tag is present.Kernel.php.Inspect Missed Tasks:
var/log/scheduler.log (if configured).telescope:record or Horizon’s "Failed Jobs" tab.Validate Cron Syntax:
->cron() with the same syntax.Handle Argument Passing:
#[AsCronTask] may fail if passing arrays as arguments (fixed in v8.0.4).new Message(new MyCommand($arg1, $arg2)).Schedule::command('my:command', ['arg1', 'arg2']).#[AsCronTask] with arguments requires new Message() wrapping.Custom Schedule Providers:
ScheduleProviderInterface to dynamically generate schedules at runtime.Schedule::call() or middleware.Event Listeners:
PostRunEvent to react to task outcomes (e.g., send Slack alerts).public static function getSubscribedEvents(): array
{
return [
PostRunEvent::class => 'onTaskRun',
];
}
JobProcessed/FailedJob events.Transport Adapters:
Testing:
SchedulerTestCase for testing schedules.Schedule::fake() in PHPUnit:
use Illuminate\Support\Facades\Schedule;
public function test_scheduled_task()
{
Schedule::fake();
$this->artisan('schedule:run');
Schedule::assertRan('command:test');
}
How can I help you explore Laravel packages today?