frequence-web/calendr-bundle
A Symfony bundle that integrates the Calendr calendar library into your application, offering ready-made services, configuration, and Twig helpers so you can build, render, and manage calendars and events with minimal setup.
Read this first: frequence-web/calendr-bundle is a Symfony bundle, not a Laravel package. As a Laravel developer, the bundle itself is mostly reference material — the real dependency you want is the underlying library, yohang/calendr. The bundle's job is wiring CalendR's Calendar factory and event providers into Symfony's container plus a Twig helper; there is no Laravel service provider, no facade, and no Blade directives.
Minimal path for a Laravel app:
composer require yohang/calendr
Then bind the factory once in a service provider:
// app/Providers/CalendarServiceProvider.php
public function register(): void
{
$this->app->singleton(\CalendR\Calendar::class, fn () => new \CalendR\Calendar());
}
Minimal path for a Symfony app (the bundle's intended home):
composer require frequence-web/calendr-bundle
Register FrequenceWeb\Bundle\CalendRBundle\FrequenceWebCalendRBundle in AppKernel::$bundles (Symfony 2/3) or config/bundles.php (Symfony 4), then check vendor/frequence-web/calendr-bundle/Resources/config/ for the service IDs it exposes and debug:container calendr to confirm.
Where to look first:
vendor/yohang/calendr/src/CalendR/Period/ — Month, Week, Day, Year, PeriodInterface. This is where 90% of your day-to-day API lives.vendor/yohang/calendr/src/CalendR/Event/ — EventInterface, Event\Collection, and the provider contract.DependencyInjection/ extension and Twig extension class — useful only if you're copying the event-provider wiring pattern into a Laravel provider.First use case: render a month grid with events.
$calendar = app(\CalendR\Calendar::class);
$month = $calendar->getMonth(2024, 6); // June 2024
$events = $this->events->forPeriod($month); // your own repository
return view('calendar.month', compact('month', 'events'));
1. Treat Calendar as a period factory, not a service. It builds Year, Month, Week, Day objects. Keep it injected or resolved from the container; never new periods directly — you lose the bundle's configuration (first day of week, etc.) and make testing harder.
2. Load events by period, not globally. The pattern the bundle encourages (and the only one that scales) is an event provider that receives a period and returns the events intersecting it. In Laravel this is a repository or query scope:
public function forPeriod(\CalendR\Period\PeriodInterface $period): iterable
{
return Event::query()
->where('starts_at', '<', $period->getEnd())
->where('ends_at', '>', $period->getStart())
->get()
->map(fn ($e) => new CalendarEvent($e));
}
3. Implement CalendR\Event\EventInterface on a thin DTO. Don't make Eloquent models implement it directly — you'll fight with getUid(), getStart(), and getEnd() semantics and leak persistence into your view layer.
final class CalendarEvent implements \CalendR\Event\EventInterface
{
public function __construct(
private string $uid,
private \DateTimeInterface $start,
private \DateTimeInterface $end,
private string $title,
) {}
public function getUid(): string { return $this->uid; }
public function getStart(): \DateTimeInterface { return $this->start; }
public function getEnd(): \DateTimeInterface { return $this->end; }
public function getTitle(): string { return $this->title; }
}
4. Render by iterating the period and asking which events it contains. This avoids pre-bucketing events into days and handles multi-day events naturally:
@foreach ($month as $day)
<div class="day">
<span>{{ $day->getStart()->format('j') }}</span>
@foreach ($events as $event)
@if ($day->contains($event->getStart()))
<span class="pill">{{ $event->getTitle() }}</span>
@endif
@endforeach
</div>
@endforeach
5. Navigation is free. $month->getNext() / $month->getPrevious() return new period objects; wire them to ?month=2024-07 query params. Parse the input once at the controller boundary and pass periods, never raw year/month pairs, into views.
6. In Symfony, register providers via the tag/compiler pass the bundle exposes. This is the one thing worth copying if you later move the app: a calendr.event_provider tag (or its equivalent in the bundle's CompilerPass) collects providers into a registry. In Laravel, mirror it with a tagged binding:
$this->app->tag([DatabaseEventProvider::class], 'calendar.providers');
$this->app->singleton(EventProviderRegistry::class, fn ($app) =>
new EventProviderRegistry($app->tagged('calendar.providers'))
);
7. Prefer DateTimeImmutable if the library version allows it. Older CalendR releases mutate DateTime internally in places; mixing mutable and immutable instances is a common source of "my event moved by a day" bugs.
yohang/calendr is also quiet. Budget for vendoring or forking, and pin versions.contains() boundary semantics. Period containment checks are typically inclusive at start and exclusive at end. Events ending exactly at midnight will appear on the following day in naive loops. Test midnight and DST boundaries explicitly.getUid() must be globally unique per event instance. Duplicate UIDs in a collection will silently deduplicate or overwrite depending on the code path. For recurring events, suffix the occurrence date.Month yields Day objects, not DateTime. Calling ->format() directly on the loop variable fails; always go through ->getStart(). This trips up almost everyone on first use.DateTime objects directly; mixing timezones produces off-by-one-day layouts that are painful to debug in a Blade template.dump the whole period. A Year iterator over days is fine; dumping it recursively will blow up your request. Log getStart()/getEnd() of the period and the count of events instead.bin/console debug:container calendr and debug:twig to confirm the bundle's services and filters are registered. In Laravel there is nothing to debug — if Calendar resolves, you're done.CalendR\Period\Period and overriding getDatePeriod() plus iteration; the bundle won't help. Event providers are the only genuinely pluggable seam, and that pattern is worth lifting into Laravel even if you never install the bundle.yohang/calendr directly, and skip the bundle. Treat frequence-web/calendr-bundle as a design reference, not a dependency.How can I help you explore Laravel packages today?