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

Cadence Laravel Package

directorytree/cadence

Cadence adds model-based scheduling to Laravel. Attach one or more cron or RRULE schedules to any Eloquent model, track due runs, and dispatch events when schedules trigger. Driver-based design supports cron, php-rrule, Recurr, or custom drivers.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Install the package and a driver (e.g., Cron):
    composer require directorytree/cadence dragonmantank/cron-expression
    
  2. Publish migrations and run them:
    php artisan vendor:publish --provider="DirectoryTree\Cadence\CadenceServiceProvider"
    php artisan migrate
    
  3. Add the HasSchedules trait to a model (e.g., Report):
    use DirectoryTree\Cadence\HasSchedules;
    use DirectoryTree\Cadence\Schedulable;
    
    class Report extends Model implements Schedulable
    {
        use HasSchedules;
    }
    
  4. Attach a schedule to a model instance:
    $report = Report::find(1);
    $report->addSchedule(new \DirectoryTree\Cadence\Drivers\CronSchedule('0 12 * * *'));
    $report->save();
    
  5. Register the schedules:run command in routes/console.php:
    Schedule::command('schedules:run')->everyMinute();
    
  6. Listen for triggered schedules via an event listener:
    // app/Listeners/GenerateReport.php
    public function handle(ScheduleTriggered $event)
    {
        $event->schedulable->generate();
    }
    

Implementation Patterns

Usage Patterns

1. Model-Based Scheduling

  • Attach schedules to any Eloquent model dynamically:
    // Schedule a weekly report at 8 AM
    $report->addSchedule(new CronSchedule('0 8 * * 1'));
    
  • Useful for user-specific, resource-specific, or campaign-specific schedules.

2. Timezone-Aware Schedules

  • Schedule events in specific timezones (e.g., user-local time):
    $schedule = new CronSchedule('0 9 * * *', 'America/New_York');
    $user->addSchedule($schedule);
    
  • Ideal for global applications where schedules must align with user timezones.

3. Complex Recurrence Rules with RRULE

  • Use RRULE for advanced scheduling (e.g., "every 3rd Friday of the month"):
    $schedule = new RruleSchedule('FREQ=MONTHLY;BYDAY=FR;BYSETPOS=3');
    $campaign->addSchedule($schedule);
    
  • Supports php-rrule or Recurr libraries (choose based on feature needs).

4. Event-Driven Workflows

  • Decouple scheduling logic from business logic using ScheduleTriggered events:
    // app/Listeners/ProcessOrder.php
    public function handle(ScheduleTriggered $event)
    {
        if ($event->schedulable instanceof Order) {
            $event->schedulable->processPayment();
        }
    }
    
  • Enables observability, retries, and queueing (e.g., ShouldQueue listeners).

5. Bulk Schedule Management

  • Add/remove schedules programmatically (e.g., during model creation/update):
    // app/Models/User.php
    public function boot()
    {
        static::created(function ($user) {
            $user->addSchedule(new CronSchedule('0 10 * * *', $user->timezone));
        });
    }
    

6. Custom Drivers for Domain-Specific Logic

  • Extend Cadence for niche use cases (e.g., "run every 3 business days"):
    class BusinessDaySchedule extends Schedule
    {
        protected function resolveNextOccurrence(CarbonInterface $after): ?CarbonInterface
        {
            $next = $after->copy()->addDays(3);
            return $next->isWeekend() ? $next->addDays(2) : $next;
        }
    }
    
  • Register the driver in AppServiceProvider:
    \DirectoryTree\Cadence\Schedule::driver('business_day', BusinessDaySchedule::class);
    

Workflows

Daily Development Workflow

  1. Define schedules in model methods:
    // app/Models/Newsletter.php
    public function addDefaultSchedules()
    {
        $this->addSchedule(new CronSchedule('0 7 * * 1', 'America/Chicago')); // Weekly at 7 AM CT
        $this->addSchedule(new RruleSchedule('FREQ=DAILY;BYHOUR=12'));       // Daily at noon UTC
    }
    
  2. Trigger schedules via API or CLI:
    php artisan schedules:run
    
    (Or let Laravel’s scheduler handle it automatically.)
  3. Debug with Tinker:
    php artisan tinker
    >>> $user = User::find(1);
    >>> $user->schedules()->first()->next_run_at
    

Deployment Workflow

  1. Test schedules locally:
    • Use php artisan schedules:run to simulate due schedules.
    • Verify ScheduleTriggered events fire correctly.
  2. Set up the scheduler:
    • Ensure schedules:run is registered in routes/console.php and scheduled (e.g., every minute).
  3. Monitor execution:
    • Log triggered schedules (e.g., in the ScheduleTriggered listener).
    • Use Laravel’s scheduler monitoring (e.g., Horizon for queues).

CI/CD Integration

  • Test migrations: Add a test to phpunit.xml:
    <env name="DB_CONNECTION" value="sqlite_memory"/>
    
  • Run migrations in CI:
    php artisan migrate --env=testing
    
  • Test schedule logic:
    // tests/Feature/SchedulesTest.php
    public function test_schedule_triggers()
    {
        $user = User::factory()->create();
        $user->addSchedule(new CronSchedule('* * * * *')); // Every minute for testing
    
        $this->artisan('schedules:run')
             ->expectsOutput('Schedule triggered');
    
        $this->assertDatabaseHas('schedules', [
            'schedulable_id' => $user->id,
            'next_run_at' => now()->addMinute(),
        ]);
    }
    

Integration Tips

1. Queueing Schedule Actions

  • Use ShouldQueue listeners to offload work to queues:
    public function handle(ScheduleTriggered $event)
    {
        dispatch(new ProcessSchedule($event->schedulable, $event->schedule));
    }
    
  • Process jobs asynchronously with Horizon or Laravel Queues.

2. Timezone Handling

  • Store user timezones in the database and use them dynamically:
    $user->addSchedule(new CronSchedule('0 9 * * *', $user->timezone));
    
  • Fallback to a default timezone (e.g., config('app.timezone')).

3. Dynamic Schedule Updates

  • Update schedules via API or admin panel:
    // app/Http/Controllers/SchedulesController.php
    public function update(UpdateScheduleRequest $request, $id)
    {
        $model = $this->findModel($request->type, $id);
        $model->updateSchedule($request->expression, $request->timezone);
        return response()->json(['success' => true]);
    }
    

4. Soft Deletes for Schedules

  • Extend the schedules table to support soft deletes:
    // app/Models/Schedule.php
    use Illuminate\Database\Eloquent\SoftDeletes;
    
    class Schedule extends Model
    {
        use SoftDeletes;
    }
    
  • Filter out soft-deleted schedules in schedules:run:
    // app/Console/Commands/RunSchedules.php
    $schedules = Schedule::whereNull('deleted_at')->get();
    

5. Testing Schedules

  • Mock ScheduleTriggered events in tests:
    $this->fake(ScheduleTriggered::class);
    $this->artisan('schedules:run');
    $this->assertNotFaked(ScheduleTriggered::class);
    
  • Use travel() to test time-based logic:
    use Carbon\Carbon;
    Carbon::setTestNow(Carbon::now()->addDay());
    
    $this->artisan('schedules:run');
    

Gotchas and Tips

Pitfalls

1. next_run_at Inaccuracy

  • Issue: If schedules:run is not executed frequently enough, next_run_at may become stale.
  • Fix: Run schedules:run every minute (or more often for high-frequency schedules). Use withoutOverlapping() to avoid duplicate runs:
    Schedule::command('schedules:run
    
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.
ilhamsyabani/laravel-volt-starter
thethunderturner/filament-latex
ghostcompiler/laravel-querybuilder
webrek/laravel-telescope-mongodb
anousss007/blatui
zatona-eg/zatona-eg-api
cocosmos/filament-sticky-save-bar
patrickbussmann/oauth2-apple
3brs/enterprise-security-bundle
anousss007/vigilance
supportpal/eloquent-model
ardenexal/fhir-models
laravel-at/laravel-image-sanitize
romalytar/yammi-audit-log-laravel
ardenexal/fhir-validation
arshaviras/weather-widget
laravel-chronicle/core
sunchayn/nimbus
daikazu/eloquent-salesforce-objects
unseen-codes/chat