whilesmart/eloquent-scheduling

Attach one-shot or recurring (RRULE) schedules to any Eloquent model and fire them through a runner event.

Maintainers

Package info

github.com/whilesmartphp/eloquent-scheduling

pkg:composer/whilesmart/eloquent-scheduling

Transparency log

Statistics

Installs: 5

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

dev-dev 2026-07-12 17:36 UTC

This package is auto-updated.

Last update: 2026-07-12 17:55:42 UTC


README

Attach a schedule to any Eloquent model and fire it when it is due. One model, two modes: a one-shot that runs once at a set time, or a recurring schedule driven by an RRULE. A runner command fires every due schedule and emits an event your app listens for, so the same substrate powers recurring transactions, reminders, digests, and recurring agent actions. Each schedule is scoped to an owner (a workspace, organization, user) through whilesmart/eloquent-owner-access.

Install

composer require whilesmart/eloquent-scheduling
php artisan migrate

Routes register automatically under the api prefix with auth:sanctum. Set SCHEDULING_REGISTER_ROUTES=false to mount them yourself.

Attaching schedules

Schedulable gives a model the schedules pointed at it (the thing being scheduled); HasSchedules gives an owner the schedules it owns.

use Whilesmart\Scheduling\Traits\Schedulable;

class Invoice extends Model
{
    use Schedulable;
}

$invoice->schedules()->create([
    'owner_type' => Workspace::class,
    'owner_id' => $workspace->id,
    'mode' => 'recurring',
    'rrule' => 'FREQ=MONTHLY;BYMONTHDAY=1',
    'timezone' => 'Europe/Berlin',
]);

next_run_at is computed when the schedule is created (from run_at for a one-shot, or the first RRULE occurrence for a recurring one) and advanced each time it fires.

Firing due schedules

Run the command every minute from the host scheduler:

// app/Console/Kernel.php (or bootstrap/app.php on Laravel 11+)
$schedule->command('schedules:run')->everyMinute();

schedules:run loads every due schedule, dispatches Whilesmart\Scheduling\Events\ScheduleDue($schedule), then advances it: a one-shot deactivates, a recurring schedule moves to its next occurrence. Listen for the event to do the actual work:

use Whilesmart\Scheduling\Events\ScheduleDue;

Event::listen(function (ScheduleDue $event) {
    $schedule = $event->schedule;      // owner, schedulable, metadata
    // post the recurring transaction, send the reminder, run the agent action...
});

Endpoints

Method Path Purpose
GET /api/schedules List (filter by mode, active, due, owner, schedulable)
POST /api/schedules Create
GET /api/schedules/{schedule} Show
PUT/PATCH /api/schedules/{schedule} Update
DELETE /api/schedules/{schedule} Soft delete

Modes

ScheduleMode: once, recurring. A one-shot needs run_at; a recurring schedule needs an rrule (any string rlanvin/php-rrule accepts, e.g. FREQ=WEEKLY;BYDAY=MO) and honours timezone.