kigkonsult/icalcreator
PHP library for creating, parsing, and managing iCalendar (.ics) data per RFC 5545/2445 and related extensions. Build calendars with events, todos, journals, freebusy, availability, timezones, participants, locations, and more.
Installation:
composer require kigkonsult/icalcreator
Ensure your composer.json targets PHP 8+ for the latest stable version (2.40+).
First Use Case:
Generate a basic .ics file for a single event:
use Kigkonsult\Icalcreator\Vcalendar;
$vcal = Vcalendar::factory(['UNIQUE_ID' => 'your-app@example.com']);
$event = $vcal->newVevent()
->setSummary('Team Meeting')
->setDtstart(new \DateTime('20231015T100000'))
->setDtend(new \DateTime('20231015T110000'));
$ics = $vcal->createCalendar();
file_put_contents('meeting.ics', $ics);
Where to Look First:
VEVENT, VTODO, VTIMEZONE).$event = $vcal->newVevent()
->setSummary('Weekly Sync')
->setDtstart(new \DateTime('20231001T090000'))
->setRrule([
Vcalendar::FREQ => Vcalendar::WEEKLY,
Vcalendar::UNTIL => new \DateTime('20231231'),
]);
setExdate() to exclude specific instances (e.g., holidays).$vcal->setTimezone('America/New_York');
$vcal->vtimezonePopulate(); // Auto-populates VTIMEZONE component
$tz = Timezone::where('id', $event->timezone_id)->first()->name;
$vcal->setTimezone($tz);
$event->setOrganizer('admin@example.com', [
Vcalendar::CN => 'Admin User',
Vcalendar::ROLE => Vcalendar::CHAIR,
]);
$event->setAttendee('user@example.com', [
Vcalendar::PARTSTAT => Vcalendar::NEEDS_ACTION,
Vcalendar::RSVP => Vcalendar::TRUE,
]);
users table:
$attendees = User::whereIn('id', $event->attendees)->get();
foreach ($attendees as $user) {
$event->setAttendee($user->email, [
Vcalendar::CN => $user->name,
Vcalendar::PARTSTAT => Vcalendar::ACCEPTED,
]);
}
$alarm = $event->newValarm()
->setAction(Vcalendar::DISPLAY)
->setTrigger('-PT1H') // 1 hour before
->setDescription('Meeting reminder!');
// Save to file
file_put_contents(storage_path('app/calendar.ics'), $vcal->createCalendar());
// Return as HTTP response (Laravel)
return response($vcal->createCalendar())
->header('Content-Type', 'text/calendar')
->header('Content-Disposition', 'attachment; filename="calendar.ics"');
Register the package and bind a factory:
// app/Providers/AppServiceProvider.php
public function register()
{
$this->app->bind(Vcalendar::class, function () {
return Vcalendar::factory([
Vcalendar::UNIQUE_ID => config('app.name'),
Vcalendar::PRODID => '-//' . config('app.name') . '//NONSGML v1//EN',
]);
});
}
Use Laravel migrations to store iCal data:
Schema::create('ical_events', function (Blueprint $table) {
$table->id();
$table->string('uid')->unique();
$table->string('summary');
$table->dateTime('dtstart');
$table->dateTime('dtend')->nullable();
$table->json('rrule')->nullable();
$table->json('attendees')->nullable();
$table->timestamps();
});
Create routes for calendar operations:
// routes/api.php
Route::get('/calendar/{event}', [CalendarController::class, 'export']);
Route::post('/calendar', [CalendarController::class, 'import']);
Timezone Mismatches:
DateTimezone objects (e.g., new DateTime('20231015T100000', new DateTimezone('America/New_York'))).vtimezonePopulate() after setting the timezone.Recurrence Rule Conflicts:
RRULE and RDATE/EXDATE can overlap. Validate with:
$vcal->validateCalendar();
UID Collisions:
UNIQUE_ID is globally unique (e.g., include a UUID or database ID):
$vcal->setUniqueId('event-' . Str::uuid()->toString());
PHP 8+ Deprecations:
create_function() or dynamic properties. Use modern syntax:
// Bad (legacy)
$event->setProperty('SUMMARY', 'Meeting');
// Good (PHP 8+)
$event->setSummary('Meeting');
Large Calendars:
$vcal->setOutputFormat(Vcalendar::OUTPUT_FORMAT_ICAL);
$vcal->setOutputCharset('UTF-8');
Validate Output:
$vcal->validateCalendar();
if ($vcal->errorCode()) {
throw new \RuntimeException($vcal->errorMessage());
}
Inspect Components:
$vcal->debugComponent(); // Dumps raw iCal data
Common Errors:
DTSTART/DTEND formats (must be DateTime objects).SUMMARY and DTSTART are set for VEVENT.Custom Properties:
$event->setXprop('X-CUSTOM', 'value', [
Vcalendar::ALTREP => 'CID:custom@example.com',
]);
Hooks for Pre/Post Processing:
$vcal->setPreProcessor(function ($component) {
if ($component instanceof \Kigkonsult\Icalcreator\Vevent) {
$component->setComment('Auto-generated by ' . config('app.name'));
}
});
Laravel Events: Trigger events when calendars are generated:
// app/Providers/EventServiceProvider.php
public function boot()
{
CalendarGenerated::dispatch($vcal->createCalendar());
}
Testing:
Mockery to stub DateTime objects:
$mockDate = Mockery::mock(\DateTime::class);
$mockDate->shouldReceive('format')->andReturn('20231015T100000');
$event->setDtstart($mockDate);
Reuse Components:
$alarm = $vcal->newValarm()->setAction(Vcalendar::DISPLAY);
$event1->setComponent($alarm);
$event2->setComponent($alarm); // Reuse alarm
Batch Processing:
$events = Event::where('is_recurring', true)->get();
$vcal = Vcalendar::factory([...]);
foreach ($events as $event) {
$vcal->addComponent($event->toIcalComponent());
}
Cache Timezones:
// Cache VTIMEZONE components
Cache::remember('vtimezone-Europe/London', 3
How can I help you explore Laravel packages today?