Download the PHP package boysfromthefactory/groundhog without Composer
On this page you can find all versions of the php package boysfromthefactory/groundhog. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Download boysfromthefactory/groundhog
More information about boysfromthefactory/groundhog
Files in boysfromthefactory/groundhog
Package groundhog
Short Description Declarative recurring Eloquent models backed by RFC 5545 rules.
License MIT
Homepage https://github.com/boysfromthefactory/groundhog
Informations about the package groundhog
Groundhog
Declarative recurring Eloquent models backed by RFC 5545 rules.
Add one trait to a model, give a record a recurrence rule, and keep querying it like any other
model: every read returns one hydrated, non-stored instance per occurrence, next to your plain
records. Pagination, counts, aggregates, ordering, whereHas, joins and eager loading all work
over the occurrences. Saving an occurrence stores it as an exception of its series; deleting one
cancels it.
Rules are parsed and expanded by rlanvin/php-rrule.
Documentation
The full guide, with examples for every feature, is in the wiki: Quick Start · Usage guide · Configuration · Errors · Limitations
Requirements
- PHP 8.4+, Laravel 13
- SQLite 3.35+, MySQL 8.4 or PostgreSQL 14+ (SQL Server is not supported)
ext-intlis recommended for localisedhumanReadable()output
Installation
Optionally publish the configuration:
The migration creates two tables: groundhog_recurrences (one rule per series) and
groundhog_exclusions (exceptions and cancellations). Occurrences are generated per query and
never stored. Your own tables need no new columns.
Upgrading from 0.1: run php artisan vendor:publish --tag="groundhog-migrations" and
php artisan migrate again. The newly published migration drops the groundhog_occurrences
table and the materialized_until column.
Declaring a recurring model
List recurrence_rule in $fillable if the model uses $fillable; $guarded models accept it
as is.
Assigning rules
A rule is attached through the recurrence_rule attribute and saved with the record:
The rule may be an RRULE string, a string with a DTSTART;TZID=… line, a php-rrule parts array
(['FREQ' => 'WEEKLY', 'BYDAY' => 'MO']) or an RRule\RRule. All RFC 5545 RRULE components are
supported. The first occurrence and the duration of every occurrence always come from the
record's own start and end; a DTSTART in the input only contributes its time zone. Occurrences
keep their local wall-clock time across daylight-saving changes in that zone.
Invalid input throws InvalidRecurrenceRule naming the offending part as soon as it is
assigned. EXDATE, RDATE and EXRULE are rejected: exclusions come from cancelling or
editing occurrences. Assign null to turn a series back into a plain record.
The rule cast
$meeting->recurrence_rule returns an RRule\RRule, so every php-rrule method works:
It reflects the rule only; cancellations and exceptions are not applied to it.
Querying
Write queries as for any model:
Each occurrence carries the series' attributes with its own start and end, reports
exists === false, has no primary key, and exposes:
An occurrence is addressed by groundhog_series_key plus groundhog_original_starts_at; both
are present on every row (null for plain records) and serialised by toArray().
A rule without COUNT or UNTIL never ends, so a query without an upper bound on the start or
end column expands it up to the configured horizon after its lower bound, or after now.
Editing, cancelling and deleting
save(), update(), increment() and decrement() on an occurrence persist it as an
exception, even without changes, and fire the usual creating/created/saved events.
Hard-deleting an exception keeps its occurrence cancelled; soft-deleting it hides it until
restore().
Editing the series record changes every occurrence that has no exception. Changing its rule or
its start detaches its exceptions as plain records and discards its cancellations; changing only
its end, or assigning the identical rule, keeps both. Deleting a series removes its rule,
occurrences and exceptions; soft-deleting it hides them until restore().
Caveats
- Lookups by primary key (
find,whereKey,destroy, route model binding, belongs-to relations pointing at the model) and query-level writes (update,delete,increment,insert,upsert, …) act on stored rows only and never create exceptions or cancellations.withoutOccurrences()gives any query that view. - Relations keyed on the model's own primary key (e.g.
hasManyattendees) are empty on occurrences, which have no key; reach them through$occurrence->series. - A custom Eloquent builder must extend
BoysFromTheFactory\Groundhog\Query\RecurringBuilder. chunkById(),lazyById()andeachById()throw while occurrences are expanded; usechunk()/lazy(), orwithoutOccurrences()for stored rows.- Stored rows streamed by
withoutOccurrences()->cursor()get the identity attributes only onceisOccurrenceException(),originalOccurrenceStart()orseriesis used; useget()orlazy()when serialising them. - A series whose rule generates more than
max_occurrences_per_seriesoccurrences between its start and the horizon is rejected on save, and a query whose window holds more than that many occurrences of one series throwsOccurrenceLimitExceeded. - Nothing derived from a rule is stored: every expanded query loads all series of the model type and generates their occurrences for its window, so cost grows with series × occurrences in the window. 1,000 daily series over a one-year window take about 5–8 s for a page of 25 with its total (SQLite, MySQL 8.4, PostgreSQL 18; see the wiki's How It Works page).
- Expanded queries on any driver other than SQLite, MySQL or PostgreSQL throw
RecurrenceNotSupported. cursorPaginate()is not supported, and occurrences cannot be queued by identity.- Bulk deletes of series rows bypass the cleanup of their rules, as Eloquent bulk deletes bypass model events.
Testing
Changelog
See CHANGELOG.
License
The MIT License (MIT). See License File.
All versions of groundhog with dependencies
illuminate/contracts Version ^13.0
rlanvin/php-rrule Version ^3.0
spatie/laravel-package-tools Version ^1.16