vincenzoraco / recurrences
A Laravel package for managing recurring events using the RFC 5545 RRULE specification
Installs: 3
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 0
Forks: 0
Open Issues: 0
pkg:composer/vincenzoraco/recurrences
Requires
- php: ^8.4
- illuminate/support: ^v11.0|^v12.0
- rlanvin/php-rrule: ^2.5
Requires (Dev)
- laravel/pint: ^1.18
- orchestra/testbench: ^9.0
- pestphp/pest: ^3.0
- pestphp/pest-plugin-laravel: ^3.0
README
A Laravel package for managing recurring events using the RFC 5545 RRULE specification. Built on top of rlanvin/php-rrule, this package provides a Laravel-native interface for working with recurring dates and events.
Why this package?
While the bare rlanvin/php-rrule library is powerful, this package offers significant advantages for Laravel applications:
- Eloquent Integration: Attach recurrence conditions to any Eloquent model using a simple trait
- Type-Safe Data Objects: All configuration is done through typed data objects with validation
- Laravel Events: Dispatchable events for when recurrence conditions are created, updated, or deleted
- MorphMany Relationship: Attach multiple recurrence rules to any model without creating additional tables
- Facade Access: Convenient facade for accessing the service layer
- Occurrence Hashing: Built-in utilities for generating unique hashes for occurrences (useful for caching)
- Exclusion Support: Easily exclude specific dates or date ranges from recurring events
Installation
You can install the package via composer:
composer require vincenzoraco/recurrences
Usage
Making a model recurrable
Add the HasRecurrences trait and Recurrable contract to any Eloquent model:
use VincenzoRaco\Recurrences\Concerns\HasRecurrences; use VincenzoRaco\Recurrences\Contracts\Recurrable; use Illuminate\Database\Eloquent\Model; class Event extends Model implements Recurrable { use HasRecurrences; }
Creating a recurring event
Use the data objects to define recurrence conditions:
use Illuminate\Support\Carbon; use VincenzoRaco\Recurrences\Enums\RecurringFrequency; use VincenzoRaco\Recurrences\DataObjects\MultipleOccurrencesConditionDataObject; use VincenzoRaco\Recurrences\DataObjects\EndingConditionUntilDataObject; $event = Event::find(1); $condition = new MultipleOccurrencesConditionDataObject( start: Carbon::parse('2024-01-01'), frequency: RecurringFrequency::WEEKLY, interval: 1, endingCondition: new EndingConditionUntilDataObject( until: Carbon::parse('2024-03-31'), ), ); app(\VincenzoRaco\Recurrences\RecurrencesService::class) ->createMultipleOccurrencesCondition($event, $condition);
Creating a one-time occurrence
Add single dates to a recurrable model:
use Illuminate\Support\Carbon; use VincenzoRaco\Recurrences\DataObjects\SingleOccurrenceConditionDataObject; $event = Event::find(1); $condition = new SingleOccurrenceConditionDataObject( date: Carbon::parse('2024-02-14'), ); app(\VincenzoRaco\Recurrences\RecurrencesService::class) ->createOneTimeOccurrenceCondition($event, $condition);
Excluding dates from a recurring event
Exclude specific dates or date ranges:
use Illuminate\Support\Carbon; use VincenzoRaco\Recurrences\DataObjects\ExcludeOneTimeOccurrenceDataObject; use VincenzoRaco\Recurrences\DataObjects\ExcludeOccurrencesRangeDataObject; // Exclude a single date $excludeSingle = new ExcludeOneTimeOccurrenceDataObject( date: Carbon::parse('2024-02-14'), ); app(\VincenzoRaco\Recurrences\RecurrencesService::class) ->createExcludeOneTimeOccurrenceCondition($event, $excludeSingle); // Exclude a range of dates $excludeRange = new ExcludeOccurrencesRangeDataObject( start: Carbon::parse('2024-12-24'), end: Carbon::parse('2024-12-26'), ); app(\VincenzoRaco\Recurrences\RecurrencesService::class) ->createExcludeOccurrencesRangeCondition($event, $excludeRange);
Getting occurrences
Retrieve occurrences from a recurrable model:
use Illuminate\Support\Carbon; use RRule\RSet; use VincenzoRaco\Recurrences\DataObjects\GetRSetOccurrencesBetweenDataObject; $event = Event::find(1); // Build the RSet from all recurrence conditions $rset = app(\VincenzoRaco\Recurrences\RecurrencesService::class) ->getRSet($event->recurrenceConditions); // Get occurrences within a date range $dataObject = new GetRSetOccurrencesBetweenDataObject( startDate: Carbon::parse('2024-01-01'), endDate: Carbon::parse('2024-01-31'), ); $occurrences = app(\VincenzoRaco\Recurrences\RecurrencesService::class) ->getRSetOccurrencesBetween($rset, $dataObject); foreach ($occurrences->getOccurrences() as $occurrence) { echo $occurrence->toDateString(); }
Using the facade
use VincenzoRaco\Recurrences\Facades\Recurrences; use Illuminate\Support\Carbon; use VincenzoRaco\Recurrences\DataObjects\GetRSetOccurrencesBetweenDataObject; $event = Event::find(1); $rset = Recurrences::getRSet($event->recurrenceConditions); $dataObject = new GetRSetOccurrencesBetweenDataObject( startDate: Carbon::parse('2024-01-01'), endDate: Carbon::parse('2024-01-31'), ); $occurrences = Recurrences::getRSetOccurrencesBetween($rset, $dataObject);
Using the facade alias
use VincenzoRaco\Recurrences\Recurrences; // Same as above, but shorter $rset = Recurrences::getRSet($event->recurrenceConditions);
Configuration
Publish the config file:
php artisan vendor:publish --provider="VincenzoRaco\Recurrences\RecurrencesServiceProvider"
Available configuration options:
return [ 'max_occurrences' => 1000, ];
Available Frequencies
RecurringFrequency::DAILYRecurringFrequency::WEEKLYRecurringFrequency::MONTHLYRecurringFrequency::YEARLY
Ending Conditions
NoEndingConditionDataObject- Never endsEndingConditionUntilDataObject- Ends on a specific dateEndingConditionTimesDataObject- Ends after a specific number of occurrences
Events
The package dispatches Laravel events for lifecycle hooks:
RecurringConditionCreatedEventRecurringConditionUpdatedEventRecurringConditionDeletedEvent
Testing
composer test
Changelog
Please see CHANGELOG for more information what has changed recently.
Contributing
Please see CONTRIBUTING for details.
Security
If you discover any security related issues, please email vincenzo [at] vincenzoraco.dev instead of using the issue tracker.
Credits
License
The MIT License (MIT). Please see License File for more information.