Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Scheduler Laravel Package

abc/scheduler

Experimental PHP scheduler library for running jobs based on CRON expressions. Define schedule providers and processors via simple interfaces, bind them in a Scheduler, and execute due schedules with an included Symfony Console command (abc:schedule).

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup in Laravel

  1. Install the package:

    composer require abc/scheduler
    
  2. Create a custom provider (e.g., app/Providers/SchedulerProvider.php):

    use Abc\Scheduler\ProviderInterface;
    use Abc\Scheduler\ScheduleInterface;
    
    class SchedulerProvider implements ProviderInterface
    {
        public function getName(): string { return 'laravel'; }
    
        public function provideSchedules(int $limit = null, int $offset = null): array
        {
            return [
                new class implements ScheduleInterface {
                    public function getCron(): string { return '* * * * *'; }
                    public function getName(): string { return 'test-schedule'; }
                    public function getCallback(): string { return 'App\Jobs\TestJob::dispatch'; }
                }
            ];
        }
    
        public function save(ScheduleInterface $schedule): void { /* Implement if needed */ }
    }
    
  3. Create a custom processor (e.g., app/Services/SchedulerProcessor.php):

    use Abc\Scheduler\ProcessorInterface;
    use Abc\Scheduler\ScheduleInterface;
    
    class SchedulerProcessor implements ProcessorInterface
    {
        public function process(ScheduleInterface $schedule)
        {
            $callback = $schedule->getCallback();
            if (class_exists($callback)) {
                eval("{$callback}"); // Or use Laravel's job dispatcher
            }
        }
    }
    
  4. Bind in a service provider (e.g., AppServiceProvider):

    use Abc\Scheduler\Scheduler;
    use Abc\Scheduler\Symfony\ScheduleCommand;
    
    public function register()
    {
        $scheduler = new Scheduler();
        $scheduler->bind(new SchedulerProvider(), new SchedulerProcessor());
    
        $this->app->singleton('scheduler', fn() => $scheduler);
        $this->app->singleton(ScheduleCommand::class, fn($app) =>
            new ScheduleCommand($app->make('scheduler'))
        );
    }
    
  5. Register the command (e.g., in app/Console/Kernel.php):

    protected $commands = [
        \Abc\Scheduler\Symfony\ScheduleCommand::class,
    ];
    
  6. Run the scheduler (via cron or Laravel's task scheduler):

    php artisan abc:schedule
    

Implementation Patterns

Workflow Integration

  1. Laravel Task Scheduling: Use Laravel's built-in scheduler (php artisan schedule:run) to trigger abc:schedule:

    // app/Console/Kernel.php
    protected function schedule(Schedule $schedule)
    {
        $schedule->command('abc:schedule')->everyMinute();
    }
    
  2. Dynamic Schedule Management: Store schedules in a database (e.g., schedules table) and fetch them in provideSchedules():

    public function provideSchedules(int $limit = null, int $offset = null): array
    {
        return DB::table('schedules')
            ->limit($limit)
            ->offset($offset)
            ->get()
            ->map(fn($record) => new DatabaseSchedule($record));
    }
    
  3. Job Dispatching: Replace eval in the processor with Laravel's job system:

    public function process(ScheduleInterface $schedule)
    {
        dispatch(new ($schedule->getCallback()));
    }
    
  4. Logging and Retries: Extend the processor to log failures and retry jobs:

    public function process(ScheduleInterface $schedule)
    {
        try {
            dispatch(new ($schedule->getCallback()));
        } catch (\Throwable $e) {
            Log::error("Schedule {$schedule->getName()} failed: " . $e->getMessage());
            // Implement retry logic (e.g., via Laravel's queue)
        }
    }
    
  5. Environment-Specific Schedules: Load schedules conditionally based on the environment:

    public function provideSchedules(int $limit = null, int $offset = null): array
    {
        $config = config("scheduler.{$this->app->environment()}");
        return collect($config)->map(fn($cron, $name) =>
            new CronSchedule($name, $cron, 'App\Jobs\\' . $name . 'Job')
        )->toArray();
    }
    

Gotchas and Tips

Pitfalls

  1. Cron Parsing Issues:

    • The package uses a basic cron parser. For complex expressions (e.g., @yearly), consider validating input or using a dedicated library like drushin/cron-expression.
  2. Race Conditions:

    • If multiple processes run abc:schedule simultaneously, schedules may execute out of order. Use Laravel's queue system to serialize execution:
      public function process(ScheduleInterface $schedule)
      {
          dispatchSync(new ProcessScheduleJob($schedule));
      }
      
  3. Callback Security:

    • Avoid using eval in production. Instead, use Laravel's job system or a whitelist of allowed callbacks.
  4. Time Zone Handling:

    • The scheduler uses the system's time zone. Ensure consistency by setting Laravel's time zone in .env:
      APP_TIMEZONE=UTC
      
  5. Database Locking:

    • If using a database-backed provider, implement row-level locking to prevent duplicate executions:
      public function save(ScheduleInterface $schedule): void
      {
          DB::table('schedules')->where('name', $schedule->getName())->lockForUpdate();
          // Save logic
      }
      

Debugging Tips

  1. Log Schedule Execution: Add logging to the processor:

    public function process(ScheduleInterface $schedule)
    {
        Log::info("Executing schedule: {$schedule->getName()}");
        // Process logic
    }
    
  2. Dry Run Mode: Extend the command to simulate execution without running callbacks:

    // In ScheduleCommand
    protected function execute(InputInterface $input, OutputInterface $output)
    {
        if ($input->getOption('dry-run')) {
            $this->scheduler->dryRun($output);
            return;
        }
        // Normal execution
    }
    
  3. Test Cron Expressions: Use a cron validator like crontab.guru to verify expressions before implementation.

Extension Points

  1. Custom Schedule Types: Extend ScheduleInterface to support additional fields (e.g., getMaxRetries()):

    interface ScheduleInterface {
        // ... existing methods
        public function getMaxRetries(): int;
    }
    
  2. Event Dispatching: Trigger Laravel events before/after processing:

    public function process(ScheduleInterface $schedule)
    {
        event(new ScheduleProcessing($schedule));
        // Process logic
        event(new ScheduleProcessed($schedule));
    }
    
  3. Rate Limiting: Implement a rate limiter in the processor to avoid overloading the system:

    use Symfony\Component\RateLimiter\RateLimiterInterface;
    
    public function __construct(private RateLimiterInterface $limiter) {}
    
    public function process(ScheduleInterface $schedule)
    {
        if (!$this->limiter->consume($schedule->getName())) {
            Log::warning("Rate limit exceeded for {$schedule->getName()}");
            return;
        }
        // Process logic
    }
    
  4. Webhook Notifications: Add a webhook endpoint to notify external systems when schedules run:

    Route::post('/scheduler/webhook', function (Request $request) {
        $schedule = $request->schedule;
        // Notify external service
    });
    
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
terminal42/code-quality-tools
codifyo/ts-generator-bundle
andydefer/laravel-cluster
testo/fiber
mintobit/jobqueue
a4sex/maintenance-bundle
a4sex/entity-date-update
a4sex/client-identifier
a4sex/base-utilites
a4sex/key-value-storage
a4sex/micro-status
chilldev/dependency-injection-extra
datinglibre/datinglibre-app-api
biberltd/corebundle
bricre/symfony-bundle-test
biberltd/logbundle
dominium/http-adapter-bundle
dominium/google-analytics
a4sex/auto-clean-entity
christhompsontldr/laravel-inky