Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Ical Laravel Package

eluceo/ical

eluceo/ical is a PHP library that lets you create RFC 5545-compliant .ics calendar files through a clean domain abstraction, with a separate presentation layer handling output. Install via Composer.

View on GitHub
Deep Wiki
Context7

Getting Started

Install with Composer and make sure your PHP version matches the currently supported window — 2.16 requires PHP 8.4–8.5, 2.15 supports 8.3–8.4, and anything below 2.14 targets 7.4–8.3. Pin the package version if your app is on an older PHP branch.

composer require eluceo/ical

Look first at three places: the documentation site (ical.poerschke.nrw), the examples/ folder in the repo, and the Domain vs Presentation split in the source. The mental model is that everything under Eluceo\iCal\Domain is what an event is, and everything under Eluceo\iCal\Presentation is how it renders to *.ics. You build domain objects, then hand them to a factory.

The fastest first use case is a download endpoint:

use Eluceo\iCal\Domain\Entity\Calendar;
use Eluceo\iCal\Domain\Entity\Event;
use Eluceo\iCal\Domain\ValueObject\Date;
use Eluceo\iCal\Domain\ValueObject\SingleDay;
use Eluceo\iCal\Presentation\Factory\CalendarFactory;

$event = (new Event())
    ->setSummary('Christmas Eve')
    ->setDescription('Lorem Ipsum Dolor...')
    ->setOccurrence(
        new SingleDay(new Date(new \DateTimeImmutable('2030-12-24')))
    );

$calendar = new Calendar([$event]);
$component = (new CalendarFactory())->createCalendar($calendar);

return response((string) $component, 200, [
    'Content-Type' => 'text/calendar; charset=utf-8',
    'Content-Disposition' => 'attachment; filename="cal.ics"',
]);

That single endpoint covers the 80% case: "give the user an .ics file for this thing." Everything else is enrichment of the Event object.

Implementation Patterns

Wrap generation in a dedicated service or action class. Don't scatter CalendarFactory calls through controllers. A CalendarBuilder that takes your Eloquent models and returns a Calendar domain object keeps the presentation layer swappable and gives you one place to test.

Email attachments. In Laravel, either build the string and use attachData($ics, 'invite.ics', ['mime' => 'text/calendar']), or write to a temp file and attach. Because the component is Stringable, (string) $component is the whole handoff — no file I/O required for the common case.

Recurring events. Set the occurrence once, then add one or more RecurrenceRule objects (addRecurrenceRule(), plural since 0.11). Chain setInterval, setCount, setUntil, and by-rules (byDay, byMonthDay, setBySetPosition for "3rd Tuesday of the month"-style rules). Verify the exact Frequency API against the docs for your installed minor — it has shifted between releases.

$event->setOccurrence(new SingleDay(new DateTime($start)));
$event->addRecurrenceRule(
    (new RecurrenceRule(Frequency::WEEKLY()))
        ->setInterval(2)
        ->setCount(10)
);

All-day vs timed events. SingleDay with a Date value object renders VALUE=DATE (no time component). SingleDay with a DateTime value object renders a zoned timestamp. MultiDay takes two Date objects and is what you want for holidays/conferences; remember DTEND is exclusive, so a one-day trip on the 24th needs an end date of the 25th.

Timezones. Don't rely on the server default. Either make every DateTimeImmutable explicitly zoned, or attach a TimeZone to the calendar via TimeZone::createFromPhpDateTimeZone() — note that method now requires explicit begin/end dates (the default arguments were deprecated in 2.1). If your events are UTC, set UTC consistently and let clients localise.

Calendar-level metadata. setProductId('//yourdomain//Your App//EN'), setName(), and setPublishedTTL('P1H') matter for podcast/subscription feeds — the TTL tells clients how often to re-poll.

Rich events. addAttendee(), setOrganizer(), setLocation(), setUrl(), addCategory(), setStatus(), and addAlarm() with a DisplayAlarm/AudioAlarm/EmailAlarm and a trigger such as new RelatedToStart(new Duration(new \DateInterval('PT15M'))) cover most real invite requirements.

Caching. For subscription feeds that change rarely, cache the rendered string keyed on a hash of the underlying data. It's cheap and removes rendering from the hot path.

Gotchas and Tips

0.x tutorials will break your code. The 2.0 rewrite separated domain from presentation and is not backwards compatible. Most Stack Overflow answers you'll find are for the old Eluceo\iCal\Component\Calendar API. Filter aggressively for "version 2" when searching.

This is a writer, not a parser. Despite "iCal creator," there's no reader — you cannot ingest an existing .ics and inspect it. If you need import, look elsewhere.

Always set a stable UID. A freshly constructed Event gets a generated identifier; if you rebuild the calendar on every request the UID can change, and mail clients will treat an updated invite as a brand-new event (duplicate entries in the user's calendar). Call setUniqueIdentifier() with something deterministic tied to your domain record.

Don't pre-escape or pre-fold. The renderer handles RFC 5545 escaping of commas, semicolons, and (since 2.14) newlines, plus 75-octet line folding. Hand-writing \, or inserting CRLF continuations will double-escape and produce corrupt output.

METHOD and iTIP scheduling aren't handled for you. Adding attendees produces ATTENDEE lines, but full Outlook/Google "accept/decline" flows expect METHOD:REQUEST plus organizer/UID consistency. If you need real scheduling semantics, expect to extend the presentation layer or post-process the output. For plain "add to calendar," attendees can often be omitted entirely.

Extend the factories rather than the output. CalendarFactory::getProperties() was made protected in 2.11 specifically so you can subclass and inject custom properties. Similarly, Presentation\Component::setComponents() lets you splice in bespoke X- properties. Reach for that before doing string surgery on the final .ics.

Watch the trailing newline and headers. Echo the component only after header() calls, and be aware that any whitespace output before the headers (a stray ?> or BOM) will corrupt the download in production while working fine locally.

Debug by inspecting the string, not by intuition. When a client rejects the file, dump (string) $component, validate it against an RFC 5545 validator, and check the line endings — the failure is almost always a timezone, a missing DTSTAMP (auto-generated at render time, so byte-identical caching is a myth), or an exclusive DTEND.

Test with string assertions. A simple feature test asserting assertStringContainsString('DTSTART;VALUE=DATE:20301224', $ics) catches most regressions, including accidental timezone drift.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
marcelgwerder/laravel-api-handler
firebrikdotcom/conveyor-laravel-broadcaster
kanata-php/conveyor-laravel-broadcaster
alish/short-message
sonrac/lumen-league-oauth2
pushery/sanctum-hardening-for-laravel
evanschleret/laravel-uuidable
yigit-serin/laravel-openvidu
adachsoft/command-executor-lib
tagixo/document-builder
tagixo/page-builder
tagixo/mail-builder
tagixo/form-builder
primix/primix
tagixo/core
alesima/laravel-azure-service-bus
tomshaw/shopcart
likyly/laravel
darthsoup/shoppingcart
tdt/core