atoolo/events-calendar-bundle
Sitepark event calendar bundle for publishing and managing events on websites, with CMS entry, web forms, database imports, and filtered search by category, keyword, and time period.
Architecture fit — low for a Laravel codebase. Despite the assessment framing, this is not a Laravel/PHP package in the general sense: it is a Symfony bundle (sitepark/atoolo-events-calendar-bundle), and the delivery mechanism is the Symfony full-stack framework. Adoption pulls in symfony/framework-bundle semantics that Illuminate does not provide: bundle classes and DependencyInjection/Extension config trees, compiler passes and tagged services, HTTP kernel event subscribers, Symfony routing/Request objects, and (likely) Twig templates and console commands. Laravel's container is PSR-11 compliant, so some constructor autowiring can be shimmed, but the configuration surface — YAML/PHP extension config, service tagging, bundle boot order — has no native Laravel equivalent. A service-provider wrapper is not a port; it is a compatibility layer with a long tail.
The bundle is not standalone. The release history shows hard coupling to sibling packages in the Atoolo family: atoolo/resource-bundle, atoolo/graphql-search-bundle, ResourceChannelFactory, "RCE" (remote content exchange) indexers. The events calendar behaves as an adapter over Sitepark's resource/content model and search index, not as a self-contained domain library. 1.18.1 reinforces this: one of its four commits is a dependency bump on the Atoolo GraphQL package, i.e. the bundle's release cadence is now partly dictated by a sibling package's cadence. Consumed without that stack, the meaningful surface area collapses to a much smaller subset. Any integration estimate must be based on that subset, not the README feature list.
What is genuinely reusable. The domain layer is the valuable asset and is comparatively framework-light: Scheduling, SchedulingBuilder, SchedulingManager, SchedulingResolver, EventDate→Scheduling DTOs, the iCal rendering/serialization path, and the GraphQL event-teaser shape. These can in principle be extracted behind a thin port (a CalendarService interface with an iCal writer and a scheduling resolver) and re-hosted inside a Laravel service provider. That is a fork-and-maintain decision, not an install decision, and it should be priced accordingly. Note that the 1.18.1 fix lands in the query/expansion layer rather than the domain core — a mild positive for the extraction path, since the extracted subset (scheduling + iCal) is untouched by this release and continues to look separable.
Technical risk — elevated, and mostly non-technical in origin.
EventDate→Scheduling, symbolicImage→symbolicAsset) and repeated fixes hit the same area — date/time semantics (gmdate vs date, endDate not falling back to beginDate, "set endDate null if endDate & endTime are unset", DST-adjacent scheduling). 1.18.1 extends the pattern to patch releases: "differentiate between expand and search by date" is a behavioural change to query semantics shipped as a patch. Consumers who had adapted to the conflated behaviour will see different result sets after a routine composer update. Repeated patching of small, critical modules at 1.6–1.7 and again at 1.18.1 signals the model is still settling well past 1.0. Timezone, recurrence, and date-window semantics are the highest-severity failure class in any calendar, and they are also the hardest to detect without exhaustive test data.phars.xml edit, with a typo in the commit subject ("upate"), indicate static-analysis configuration is still being experimented with rather than fixed. That is not a product risk by itself, but it weakens any inference you might draw from "PHPStan at level 9" as a stable quality guarantee — the analysis baseline itself moves between releases.dba438a).Key questions to resolve before committing engineering time
atoolo/resource-bundle and atoolo/graphql-search-bundle, or are they hard requires? What is the real transitive dependency graph and its Symfony version constraints? The 1.18.1 graphQL dependency bump makes this more pressing, not less.atoolo-e2e-test), the analysis config is still being tuned, and the search-vs-date behaviour change in 1.18.1 is exactly the class of regression a contract test should have caught upstream. Validate this repo's own suite.Stack fit. PHP 8.2–8.4 is well aligned with modern Laravel, and PHP 8.1 is nominally supported but explicitly marked as no longer auto-checked — treat 8.1 as unsupported. The binding constraint is the Symfony component version range: Laravel 10/11 ship Symfony 6.x/7.x components, and installing symfony/framework-bundle alongside illuminate/* in the same application is technically possible but operationally unhealthy — competing kernels, competing routing, competing container compilation, and two config caches to invalidate. Do not co-host the bundle inside the Laravel app. Every viable path separates them.
Three viable paths, in order of preference for a Laravel shop:
Sidecar service (recommended if you need this capability soon). Deploy the bundle as a minimal standalone Symfony application — its own container, its own deploy unit — exposing the surfaces it already provides: the iCal API (/api/ical/...), the GraphQL search/teaser, and read endpoints. Laravel consumes it over HTTP with a typed client, caches responses, and owns presentation, auth, and business rules. Benefits: no framework conflict, upgrade isolation, the vendor's own stack is exercised as designed, and a future replacement is a swap of one HTTP client. Costs: two runtimes, two deploy pipelines, an extra network hop, and shared-nothing caching to design.
Domain extraction (recommended if the calendar is core to the product). Fork under MIT, lift Scheduling/SchedulingBuilder/SchedulingManager/SchedulingResolver and the iCal serializer into an internal package with a framework-agnostic facade; re-host in a Laravel service provider; implement persistence with Eloquent. This gets you the genuinely valuable 20% and discards the Atoolo-specific 80%. Costs: you own the code, the bugs, and the timezone test suite forever — budget for it explicitly.
Specification only (recommended if the calendar is a differentiator). Treat the README, release notes, and GraphQL/iCal contracts as a requirements document and build natively. Fastest to a clean domain model for a Laravel team; slowest to first demo. Note that recurring-event semantics, DST handling, and RFC 5545 conformance are the parts people underestimate — the release history here, 1.18.1 included, is a useful map of exactly which edge cases bite.
Migration path. Whichever path you pick, the sequencing is the same: pin an exact tag after verifying it, wrap every vendor touchpoint behind an internal interface (CalendarGateway), and write contract tests against the external surface — iCal output validated against RFC 5545, scheduling resolved against a fixture set covering DST boundaries, all-day events, missing endTime, and multi-day spans, plus result-set fixtures for date-filtered search versus expanded occurrences. Those tests are the asset that makes any future re-platform cheap.
Compatibility notes. Assume multi-instance support (added in 1.1.0) means per-tenant configuration — verify how config is scoped before assuming a single global calendar. Assume the indexer is cron/console-driven and therefore needs a scheduler equivalent. Assume behaviour changes can land in patch releases: 1.18.1 alters expand-vs-date-search handling, so treat ~1.18.1 as unsafe as well as ^1.x. Pin to exact versions with a review gate that applies to patches too.
Sequencing
CalendarGateway interface, write contract tests — including parameterised cases that distinguish "expand occurrences" from "search by date".Do not start at step 5. The authoring/import layer is where the largest share of the bundle's complexity and the smallest share of your differentiation lives.
Maintenance. You inherit a vendor's release cadence, not a community's. The 1.x line moves frequently and includes behaviour changes in both minor and patch releases — renames, config key substitutions (highlightCategory → simpleCategoryMap), semantics fixes in date handling, and now expand/date-search differentiation at 1.18.1. Pin exact versions, read every changelog including patch notes, and run the contract tests before any bump. Budget a recurring small but non-zero maintenance allocation (roughly 1–2 engineer-days per quarter for a sidecar deployment, plus a review gate on every upstream release; substantially more — effectively co-ownership — if you fork). The MIT license means the code will never disappear, but the maintenance is not free even when the code is.
Support. No community tier exists. Zero stars and zero dependents means no forum answers, no third-party tutorials, and no prior art when you hit an edge case. Documentation lives on the Sitepark docs site and is written for users of the Atoolo CMS, not for external integrators — expect to read source to answer configuration questions; 1.18.1's terse "differentiate between expand and search by date" is a live example of a changelog line that does not tell an integrator what actually changed. Escalation is a commercial conversation with the vendor, not a GitHub issue. If this capability is business-critical, negotiate a support arrangement before adoption, or accept that you are self-supporting from day one.
Scaling. The bundle is not the bottleneck; the search index behind it is. Indexing is batch-oriented (cron/console) and the RCE import path suggests periodic full or delta syncs — so scaling is about index freshness, job duration, and avoiding overlapping runs, not about request throughput. On the request path, iCal generation is comparatively expensive (serialization plus per-event scheduling resolution) and should be cached aggressively per query/occurrence-set, with cache keys covering language, the expand-vs-search mode, and the occurence parameter added in 1.9.0. The sidecar topology adds a network hop and a second cache to keep coherent — plan for a shared cache or short TTLs.
Failure modes to instrument from day one
date→gmdate migration, endDate incorrectly falling back to beginDate, and null-end-date handling. A wrong event date is a user-visible, trust-destroying defect. Assert on absolute timestamps in tests, never on formatted strings.Ramp-up. For an engineer already fluent in Symfony: roughly 1–2 weeks to productive, longer to confident on scheduling and query semantics. For a Laravel-only team: 3–5 weeks before they can debug the bundle rather than merely configure it — they must learn bundle extension config, tagged services, and the Atoolo resource model on top of an unfamiliar domain. The sidecar path reduces this substantially because the team can treat it as a black box with an HTTP contract. The extraction path shifts the cost from ramp-up to sustained ownership. Be honest in planning about which of those three costs you are actually signing up for: the license is free, the integration is not, and the cheapest-looking option (drop it into the Laravel app) is the one that will cost the most.
How can I help you explore Laravel packages today?