simshaun/recurr
PHP library for RFC5545 RRULE recurrence rules. Build rules from strings or fluent setters, then transform them into DateTime occurrences via transformers. Useful for calendars and recurring events with time zone support.
composer require simshaun/recurr
use Recurr\Rule;
use Recurr\Transformer\ArrayTransformer;
$rule = new Rule('FREQ=DAILY;COUNT=5', new \DateTime());
$transformer = new ArrayTransformer();
$recurrences = $transformer->transform($rule);
// Example: Fetch monthly meetings for 3 months
$startDate = new \DateTime('2023-10-01');
$rule = new Rule('FREQ=MONTHLY;COUNT=3', $startDate);
$transformer = new ArrayTransformer();
$recurrences = $transformer->transform($rule);
foreach ($recurrences as $recurrence) {
echo $recurrence->getStart()->format('Y-m-d') . "\n";
}
$rule = new Rule('FREQ=WEEKLY;BYDAY=MO,WE,FR;UNTIL=2023-12-31', $startDate);
$rule = (new Rule)
->setFreq('WEEKLY')
->setByDay(['MO', 'WE', 'FR'])
->setUntil(new \DateTime('2023-12-31'))
->setStartDate($startDate);
Generating Recurrences for a Calendar:
$transformer = new ArrayTransformer();
$recurrences = $transformer->transform($rule);
// Filter for a specific month
$filtered = $recurrences->startsBetween(
new \DateTime('2023-11-01'),
new \DateTime('2023-11-30')
);
Handling Timezones:
$rule = new Rule('FREQ=DAILY;COUNT=7', $startDate, null, 'Europe/London');
Combining Rules with Constraints:
$constraint = new BeforeConstraint(new \DateTime('2023-11-15'));
$recurrences = $transformer->transform($rule, $constraint);
Service Provider Binding:
// app/Providers/AppServiceProvider.php
public function register()
{
$this->app->bind(\Recurr\Rule::class, function ($app) {
return new \Recurr\Rule('FREQ=DAILY', new \DateTime());
});
}
Eloquent Model Events:
// app/Models/Event.php
protected static function booted()
{
static::created(function ($event) {
if ($event->is_recurring) {
$rule = new Rule($event->rrule, $event->start_date);
$transformer = new ArrayTransformer();
$recurrences = $transformer->transform($rule);
// Store or process recurrences
}
});
}
API Responses:
// app/Http/Controllers/EventController.php
public function getRecurrences(Event $event)
{
$rule = new Rule($event->rrule, $event->start_date);
$transformer = new ArrayTransformer();
$recurrences = $transformer->transform($rule);
return response()->json($recurrences->toArray());
}
Monthly Rules with Varying Days:
enableLastDayOfMonthFix() in ArrayTransformerConfig.Infinite Loops:
COUNT or UNTIL can generate infinite recurrences.ArrayTransformerConfig or use constraints.Timezone Mismatches:
Rule constructor.Daylight Saving Time:
Inspect Rule Strings:
echo $rule->getString(); // Verify rule correctness
Limit Recurrences for Testing:
$config = new ArrayTransformerConfig();
$config->setLimit(5); // Test with a small subset
$transformer->setConfig($config);
Check for Valid RRULEs:
Custom Transformers:
class CustomTransformer extends ArrayTransformer
{
public function transform(Rule $rule, ConstraintInterface $constraint = null)
{
$recurrences = parent::transform($rule, $constraint);
// Custom logic here
return $recurrences;
}
}
Custom Constraints:
class CustomConstraint implements ConstraintInterface
{
public function isAllowed(Recurrence $recurrence)
{
// Custom logic to allow/deny recurrences
return true;
}
}
Override Text Transformer:
class CustomTextTransformer extends TextTransformer
{
protected function getTranslation($key, array $params = [])
{
// Custom translations
return parent::getTranslation($key, $params);
}
}
Virtual Limit:
ArrayTransformerConfig or use COUNT/UNTIL in rules.Lazy Loading:
Caching:
$cacheKey = 'recurrences_' . md5($rule->getString());
$recurrences = Cache::remember($cacheKey, now()->addHours(1), function () use ($rule) {
return $transformer->transform($rule);
});
Store Rules in Database:
// Migration
Schema::create('events', function (Blueprint $table) {
$table->id();
$table->string('rrule');
$table->dateTime('start_date');
$table->dateTime('end_date')->nullable();
$table->string('timezone')->default('UTC');
});
Use Accessors:
// app/Models/Event.php
public function getRecurrencesAttribute()
{
$rule = new Rule($this->rrule, $this->start_date, $this->end_date, $this->timezone);
return (new ArrayTransformer())->transform($rule);
}
Query Scopes:
// app/Models/Event.php
public function scopeActive($query)
{
return $query->where(function ($q) {
$q->whereNull('end_date')
->orWhere('end_date', '>', now());
});
}
How can I help you explore Laravel packages today?