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

Cadence provides a driver-based scheduling system for your Eloquent models using cron expressions or RRULE recurrence patterns. Attach one or many schedules to any model, and Cadence will track and dispatch events when they're due.

Index

Requirements

  • PHP >= 8.2
  • Laravel >= 11.0

Installation

You can install the package via composer:

composer require directorytree/cadence

Then, install at least one schedule driver:

# Cron (recommended for simple schedules)
composer require dragonmantank/cron-expression

# RRULE via php-rrule
composer require rlanvin/php-rrule

# RRULE via Recurr
composer require simshaun/recurr

Publish and run the migrations:

php artisan vendor:publish --provider="DirectoryTree\Cadence\CadenceServiceProvider"
php artisan migrate

This creates a schedules table with the following columns:

  • schedulable_type / schedulable_id — polymorphic relation to your model
  • type — the driver type (e.g. cron, rrule, recurr)
  • expression — the schedule expression
  • timezone — optional timezone for the schedule
  • next_run_at — precomputed next occurrence for efficient querying
  • last_run_at — timestamp of the last run

Setup

Implement the Schedulable interface and use the HasSchedules trait on any model you want to schedule:

// app/Models/Report.php

namespace App\Models;

use DirectoryTree\Cadence\HasSchedules;
use DirectoryTree\Cadence\Schedulable;
use Illuminate\Database\Eloquent\Model;

class Report extends Model implements Schedulable
{
    use HasSchedules;
}

Usage

Adding Schedules

Create a driver instance and add it to your model:

use DirectoryTree\Cadence\Drivers\CronSchedule;

$report = Report::find(1);

// Every day at noon
$report->addSchedule(new CronSchedule('0 12 * * *'));

// Every Monday at 9am
$report->addSchedule(new CronSchedule('0 9 * * 1'));

With RRULE expressions:

use DirectoryTree\Cadence\Drivers\RruleSchedule;

// Every weekday
$report->addSchedule(new RruleSchedule('FREQ=DAILY;BYDAY=MO,TU,WE,TH,FR'));

// Monthly on the 15th, starting from a specific date
$report->addSchedule(new RruleSchedule('DTSTART=20260101T000000;FREQ=MONTHLY;BYMONTHDAY=15'));

Timezones

Schedules can be timezone-aware. Pass the timezone as the second argument:

// Every day at 9am Eastern
$report->addSchedule(
    new CronSchedule('0 9 * * *', 'America/New_York')
);

Or set it via the setTimezone() method:

$schedule = new CronSchedule('0 9 * * *');

$schedule->setTimezone('America/New_York');

$report->addSchedule($schedule);

Running Due Schedules

Register the schedules:run command in your application's scheduler to run every minute:

// routes/console.php

use Illuminate\Support\Facades\Schedule;

Schedule::command('schedules:run')
    ->withoutOverlapping()
    ->everyMinute();

This command queries all schedules where next_run_at <= now(), dispatches a ScheduleTriggered event for each, and advances next_run_at to the next occurrence.

Listening for Triggered Schedules

Listen for the ScheduleTriggered event to perform work when a schedule fires:

namespace App\Listeners;

use App\Models\Report;
use Illuminate\Contracts\Queue\ShouldQueue;
use DirectoryTree\Cadence\Events\ScheduleTriggered;

class GenerateReport implements ShouldQueue
{
    public function shouldQueue(ScheduleTriggered $event): bool
    {
        return $event->schedule->schedulable_type = Report::class;
    }

    public function handle(ScheduleTriggered $event): void
    {
        $event->schedulable->generate();
    }
}

Register it in your EventServiceProvider or use event discovery.

Drivers

Cadence uses a driver-based architecture. Drivers are automatically registered when their backing library is installed.

Cron

Requires dragonmantank/cron-expression:

use DirectoryTree\Cadence\Drivers\CronSchedule;

new CronSchedule('0 12 * * *');         // Every day at noon
new CronSchedule('*/15 * * * *');       // Every 15 minutes
new CronSchedule('0 9 * * 1-5');        // Weekdays at 9am

RRULE (php-rrule)

Requires rlanvin/php-rrule:

use DirectoryTree\Cadence\Drivers\RruleSchedule;

new RruleSchedule('FREQ=DAILY;BYDAY=MO,TU,WE,TH,FR');
new RruleSchedule('FREQ=MONTHLY;BYMONTHDAY=1;COUNT=12');

RRULE (Recurr)

Requires simshaun/recurr:

use DirectoryTree\Cadence\Drivers\RecurrSchedule;

new RecurrSchedule('FREQ=WEEKLY;BYDAY=MO,WE,FR');
new RecurrSchedule('FREQ=YEARLY;BYMONTH=1;BYMONTHDAY=1');

Custom Drivers

Create a class that extends the base Schedule driver:

namespace App\Drivers;

use Carbon\CarbonInterface;
use DirectoryTree\Cadence\Drivers\Schedule;

class CustomSchedule extends Schedule
{
    protected function resolveNextOccurrence(CarbonInterface $after): ?CarbonInterface
    {
        // Your recurrence logic here
    }
}

Then register it in your AppServiceProvider:

use App\Drivers\CustomSchedule;
use DirectoryTree\Cadence\Schedule;

Schedule::driver('custom', CustomSchedule::class);

Customizing Drivers

Each driver exposes a static tap method to configure the underlying library instance before it's used:

use Cron\CronExpression;
use DirectoryTree\Cadence\Drivers\CronSchedule;

CronSchedule::tap(function (CronExpression $cron) {
    // Configure the CronExpression instance
});
use Recurr\Rule;
use Recurr\Transformer\ArrayTransformer;
use Recurr\Transformer\ArrayTransformerConfig;
use DirectoryTree\Cadence\Drivers\RecurrSchedule;

RecurrSchedule::tap(function (Rule $rule, ArrayTransformer $transformer) {
    $transformer->setConfig(
        (new ArrayTransformerConfig)->enableLastDayOfMonthFix()
    );
});

Pass null to clear the callback:

RecurrSchedule::tap(null);
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