PHPackages                             crumbls/timeline - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. crumbls/timeline

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

crumbls/timeline
================

A scheduling and occurrence engine for Laravel

v0.0.1(1mo ago)01MITPHPPHP ^8.2

Since May 31Pushed 1mo agoCompare

[ Source](https://github.com/Crumbls/timeline)[ Packagist](https://packagist.org/packages/crumbls/timeline)[ RSS](/packages/crumbls-timeline/feed)WikiDiscussions main Synced 1w ago

READMEChangelog (1)Dependencies (7)Versions (2)Used By (0)

crumbls/timeline
================

[](#crumblstimeline)

A scheduling and occurrence engine for Laravel.

This package is not a calendar UI. It handles the domain model behind recurring events: defining schedules as RRULE strings, pre-generating concrete occurrence records, and giving you a clean query interface to drive any calendar or scheduling feature you build.

---

Requirements
------------

[](#requirements)

- PHP 8.2+
- Laravel 10, 11, or 12

---

Installation
------------

[](#installation)

```
composer require crumbls/timeline
php artisan vendor:publish --tag=timeline-migrations
php artisan migrate
```

---

Core Concepts
-------------

[](#core-concepts)

**Event** — the conceptual thing. "Laravel Meetup" or "Board Meeting". Not a specific date.

**Rule** — defines when an Event occurs. Stores an RFC 5545 RRULE string. One Event can have multiple Rules.

**Occurrence** — a concrete scheduled instance. "Laravel Meetup on June 5th." This is the primary query model.

**OccurrenceException** — a one-off modification to a single Occurrence (cancel, reschedule, modify). Never modifies the parent Rule.

**Location** — a reusable venue attached to Occurrences.

---

RRuleBuilder
------------

[](#rrulebuilder)

Writing RRULE strings by hand is error-prone. Use the fluent builder:

```
use Crumbls\Timeline\Support\RRuleBuilder;

// Every Tuesday
RRuleBuilder::make()->weekly()->onDays('TU')->toString();

// First Friday of the month
RRuleBuilder::make()->monthly()->onNthWeekday(1, 'FR')->toString();

// Every other Monday, 20 times
RRuleBuilder::make()->weekly()->every(2)->onDays('MO')->count(20)->toString();

// Human-readable description of any RRULE
RRuleBuilder::describe('FREQ=WEEKLY;BYDAY=MO,WE,FR');
// "weekly on Monday, Wednesday and Friday"
```

See [IMPLEMENTATION.md](IMPLEMENTATION.md) for the full builder reference.

---

Quick Start
-----------

[](#quick-start)

```
use Crumbls\Timeline\Models\Event;
use Crumbls\Timeline\Models\Rule;
use Crumbls\Timeline\Enums\EventStatus;

// Create an event
$event = Event::create([
    'name'     => 'Laravel Meetup',
    'timezone' => 'America/Denver',
    'status'   => EventStatus::Published,
]);

// Add a weekly rule — occurrences generate automatically
Rule::create([
    'event_id'  => $event->id,
    'starts_at' => '2025-06-03 18:00:00',
    'ends_at'   => '2025-06-03 20:00:00',
    'rrule'     => 'FREQ=WEEKLY;BYDAY=TU',
]);

// Query occurrences
use Crumbls\Timeline\Models\Occurrence;
use Carbon\Carbon;

$feed = Occurrence::between(Carbon::parse('2025-06-01'), Carbon::parse('2025-06-30'))
    ->scheduled()
    ->with(['event', 'location'])
    ->orderBy('starts_at')
    ->get();
```

---

Occurrence Scopes
-----------------

[](#occurrence-scopes)

```
Occurrence::upcoming()->get();
Occurrence::today()->get();
Occurrence::between($start, $end)->get();
Occurrence::scheduled()->get();
Occurrence::forEvent($event->id)->get();
Occurrence::atLocation($location->id)->get();
```

Scopes chain freely:

```
Occurrence::scheduled()->upcoming()->forEvent($event->id)->paginate(20);
```

---

Configuration
-------------

[](#configuration)

```
// config/timeline.php
return [
    'table_prefix'                 => 'timeline_',
    'occurrence_generation_months' => 12,
    'default_timezone'             => 'UTC',
];
```

All table names are resolved through `table_prefix`. Change it before running migrations.

---

How Occurrence Generation Works
-------------------------------

[](#how-occurrence-generation-works)

When a Rule is saved, `GenerateOccurrencesJob` is dispatched. The `OccurrenceGenerator` service expands the RRULE into concrete dates for the configured window (default: 12 months), then:

- Creates new Occurrence records for dates not already present
- Updates `ends_at` on existing records if the duration changed
- Deletes future `Scheduled` occurrences no longer in the expansion
- Leaves `Cancelled` and `Completed` occurrences untouched

You can also trigger generation manually:

```
use Crumbls\Timeline\Services\OccurrenceGenerator;

app(OccurrenceGenerator::class)->generate($rule);
```

---

Testing
-------

[](#testing)

```
composer test
```

56 Pest tests covering events, rules, occurrences, generator logic, and exceptions.

---

Further Reading
---------------

[](#further-reading)

See [IMPLEMENTATION.md](IMPLEMENTATION.md) for a full implementation guide including calendar feed construction, location usage, exception handling, queue setup, and an RRULE reference table.

---

License
-------

[](#license)

MIT

###  Health Score

34

—

LowBetter than 75% of packages

Maintenance89

Actively maintained with recent releases

Popularity1

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity36

Early-stage or recently created project

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

54d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/3020753?v=4)[Chase C. Miller](/maintainers/chasecmiller)[@chasecmiller](https://github.com/chasecmiller)

---

Top Contributors

[![chasecmiller](https://avatars.githubusercontent.com/u/3020753?v=4)](https://github.com/chasecmiller "chasecmiller (1 commits)")

###  Code Quality

TestsPest

### Embed Badge

![Health badge](/badges/crumbls-timeline/health.svg)

```
[![Health](https://phpackages.com/badges/crumbls-timeline/health.svg)](https://phpackages.com/packages/crumbls-timeline)
```

###  Alternatives

[psalm/plugin-laravel

Psalm plugin for Laravel

3345.3M347](/packages/psalm-plugin-laravel)[illuminate/broadcasting

The Illuminate Broadcasting package.

7127.2M221](/packages/illuminate-broadcasting)[flarum/core

Delightfully simple forum software.

211.4M2.4k](/packages/flarum-core)[api-platform/laravel

API Platform support for Laravel

58174.6k17](/packages/api-platform-laravel)[fleetbase/core-api

Core Framework and Resources for Fleetbase API

1235.9k21](/packages/fleetbase-core-api)[aedart/athenaeum

Athenaeum is a mono repository; a collection of various PHP packages

255.2k](/packages/aedart-athenaeum)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
