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

Groundhog Laravel Package

boysfromthefactory/groundhog

Groundhog is a lightweight Laravel/PHP package from boysfromthefactory that simplifies recurring, repeatable work in your app. Drop it in, configure a few options, and keep repetitive logic clean, predictable, and easy to test. No extra dependencies.

View on GitHub
Deep Wiki
Context7

Product Decisions This Supports

  • Build vs. buy for recurrence expansion. This package owns the hard part of calendar/scheduling products — expanding recurrence rules into concrete occurrences for an arbitrary time window — so the team can spend effort on product surface (booking, availability, reminders) rather than on RRULE edge cases.
  • Feature feasibility for "any date range" views. v0.2.0 removes the read ceiling: users can jump to next quarter, next year, or "all of 2027" and get correct results, because occurrences are computed per query instead of pre-materialized. This unlocks roadmap items like far-future planning views, long-range exports, and date-picker previews that were previously bounded.
  • Operational simplicity as a deliberate trade. Dropping the groundhog_occurrences table and materialized_until column means no cron, no queue worker, no backfill job, no drift between stored and computed data. That is a real headcount-and-on-call decision, not just a schema change: fewer moving parts, fewer "occurrences are stale" bug reports.
  • Correctness on edit semantics. Changing only a series' duration no longer triggers a rebuild; ends update on the next read. PM implication: edits are instantly consistent for end-times without an expensive write path — good for UX around "this meeting is now 45 minutes."
  • Database strategy and portability. Expansion now depends on driver-specific JSON table functions (SQLite json_each, MySQL JSON_TABLE, PostgreSQL json_array_elements). Any roadmap item that assumes SQL Server, MariaDB variants, or a second database target is now constrained; RecurrenceNotSupported is thrown elsewhere. This is a decision input for platform standardization, not a detail.
  • Performance budgeting for calendar surfaces. Published numbers: ~1,000 daily series over a one-year window costs 4.8–7.4 s (SQLite), 7.0–7.9 s (MySQL 8.4), 6.8–7.1 s (PostgreSQL 18) for a page of 25 plus a total. Cost scales with series × occurrences-in-window and is paid on every query. This should drive decisions on caching layers, default window sizes, pagination defaults, and whether "total count" is shown at all.
  • Resource-guardrail configuration. groundhog.max_occurrences_per_series now bounds per-series, per-query generation (counting only in-window occurrences). This is the knob for protecting the app from a pathological series (e.g., a minutely rule) without breaking legitimate far-future reads — worth exposing as a product-level limit with a user-facing error message.

When to Consider This Package

Adopt when:

  • The product needs recurring scheduling (calendars, availability, recurring bookings, maintenance windows, subscription-like cadences) and the team does not want to own rule expansion.
  • Queries are read-moderate and page-sized — a handful of calendar renders per user session, not a public high-QPS occurrence API.
  • Windows are bounded in practice (a month, a quarter, a year) and series counts per model type are in the hundreds to low thousands.
  • The app runs on SQLite, MySQL 8+, or PostgreSQL — and there is no plan to add another driver.
  • The team values "no background jobs, no denormalized table to keep in sync" over raw read latency, and can add caching at the application layer if needed.

Look elsewhere or plan mitigation when:

  • Sub-100 ms occurrence reads at high concurrency are a hard requirement — every query pays full expansion cost, so this needs a cache or a materialized read model, which is exactly what v0.2.0 removed.
  • Series counts per model type are in the tens of thousands, or rules are high-frequency (hourly/minutely), where series × occurrences-in-window explodes.
  • You need push-style behavior tied to occurrences (reminders, notifications) — v0.2.0 deliberately has no durable occurrence record to reference or mark as processed.
  • You need cross-driver or multi-tenant-per-driver flexibility, including SQL Server.
  • You need occurrence-level mutation (per-instance overrides, cancellations, attendance) — with no stored occurrence table, that state must live in your own domain model keyed by series + timestamp.

Upgrade note: v0.2.0 is breaking. It removes the groundhog_occurrences table, groundhog_recurrences.materialized_until, and the max_materialization_ahead config. Upgrading from 0.1 requires php artisan vendor:publish --tag="groundhog-migrations" then php artisan migrate; the shipped migration is a no-op on fresh installs. Budget this as a deploy-window item with a rollback plan, and re-baseline any performance expectations set during 0.1.

How to Pitch It (Stakeholders)

For executives: We can ship recurring-schedule features — including far-future views and instant edits — without building or maintaining a scheduling engine ourselves, and without running a background job to precompute occurrences. The trade is that every calendar read does the expansion work at query time: roughly 5–8 seconds for an extreme case (1,000 recurring series over a full year) and proportionally less for normal windows. That is a caching and UX question we control, not a correctness risk. The release also drops a database table and its sync job, which reduces operational surface area. One constraint to accept: it works on SQLite, MySQL, and PostgreSQL only, so it locks in our database choice.

For engineering: v0.2.0 replaces materialization with per-query expansion. All series of a model type are loaded, each rule expanded for the requested window, and the rows passed to the database as a single JSON parameter using json_each (SQLite), JSON_TABLE (MySQL), or json_array_elements (PostgreSQL); other drivers throw RecurrenceNotSupported. There is no read ceiling and no materialized_until. max_occurrences_per_series now caps per-series, per-query generation using only in-window occurrences — this is our abuse guardrail, and it should fail with a surfaced, user-friendly message. Duration-only edits no longer rebuild anything. Cost is O(series × occurrences in window) on every read, so if a surface needs to be fast, add a response/config cache rather than reintroducing materialization. Upgrading from 0.1 is a two-step migration (publish groundhog-migrations, then migrate) that drops the old table and column; treat it as a breaking deploy and re-run your own benchmarks, since 0.1-era performance assumptions no longer apply.

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.
payerurl/telegrambot-crypto-payment-instant-payout
dskripchenko/laravel-hybrid-search
riorizkyrainey/lara-ced
mehedimi/static-vel
zharp/zharp
nonsapiens/bigquery-model-sync
lgu-bayambang-ict-office/lgu-laravel-development-standards
andydefer/laravel-support
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel
draftsmaninc/draftsman
square-bit/laravel-volition
himito/laravel-mailman
htinlynn/genius-helper
maco-studios/hephaestus
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk