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

Zap Laravel Package

laraveljutsu/zap

Zap is a calendar and scheduling package for Laravel. Define availabilities, appointments, blocked times, and custom schedules for any resource like doctors, rooms, or employees. Handles overlaps, recurrence patterns, and availability queries. PHP 8.5+ and Laravel 13+.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Architecture fit

Zap is a domain model + query engine for temporal availability, not a booking UI. It slots in as an infrastructure concern behind whatever booking flow already exists.

  • Polymorphic attachment model. HasSchedules is a trait applied to any Eloquent model, backed by a schedulable morph. One package serves doctors, rooms, employees, and equipment without schema-per-resource. This is the right shape for multi-tenant or multi-resource scheduling products.
  • Four-type schedule taxonomy (availability / appointment / blocked / custom) with explicit overlap semantics per type. This is a meaningful design decision: availability rows are permitted to overlap each other, appointments and blocks are exclusive. It maps cleanly to how operations teams actually think ("open hours", "booked", "blackout").
  • Periods as a sub-entity. A single schedule row holds multiple time windows (09:00–12:00, 14:00–17:00), which avoids row explosion for split shifts and is the correct normalization.
  • Recurrence is the differentiator. Daily, weekly, odd/even weeks, biweekly with anchor date, monthly-by-day-of-month, bimonthly/quarterly/semiannual/annual with start_month offsets, ordinal weekday (lastMondayOfMonth), and dynamic N-week / N-month intervals. Teams typically either hand-roll this (bug farm) or integrate a generic RRULE library with awkward Laravel ergonomics. Covering ordinal-weekday and N-week anchors natively is disproportionately valuable for HR/shift and clinic use cases.
  • The "product" API is getBookableSlots($date, $duration, $buffer) plus getNextBookableSlot, isBookableAt, isBookableAtTime, and findConflicts/hasConflicts. That is the surface we would actually expose to a UI; the builder API is authoring-only. This separation is good — it means slot computation can be swapped or cached independently of schedule storage.

Integration feasibility

Low code footprint, but gated by version constraints.

  • Install is composer require → publish provider → publish migrations/config → migrate, then one trait per schedulable model. That is a sub-day integration for a greenfield resource type.
  • Critical discrepancy to resolve before planning. The README badges declare PHP ≥8.5 and Laravel ≥13.0, while the v1.9.0 release notes state CI runs PHP 8.2/8.3/8.4/8.5. These cannot both be true of composer.json. The first action item is reading the actual constraint block, because it determines whether this is installable on our current Laravel 11/12 + PHP 8.3 estate or requires a runtime upgrade first. If the README badge is accurate, adoption is effectively blocked until we are on Laravel 13 — a multi-quarter dependency, not a sprint task.
  • UUID/ULID path is a documented foot-gun. Using non-integer keys requires extending Zap\Models\Schedule and SchedulePeriod, adding HasUuids, repointing models.schedule/models.schedule_period in config, and rewriting id() → uuid('id'), morphs → uuidMorphs, foreignId('schedule_id') → foreignUuid — before running migrations. Any team that migrates first and reads the docs second ships a broken schema. Most modern Laravel codebases use UUIDs, so this is the likely default path, not the edge case.

Technical risk

  • Version constraint risk (high). PHP ≥8.5 / Laravel ≥13.0 pins the package to a bleeding-edge stack. It also implies the maintainer is not targeting the installed base, which correlates with fewer real-world issue reports on the versions we would run.
  • Bus factor (high). Single maintainer (Ludovic Guénet), personal email as the security contact, no disclosed CVE process. 0 dependents and no star signal means no evidence of production hardening by third parties.
  • Pre-1.x-in-spirit API churn (medium). isAvailableAt() is already deprecated in favor of isBookableAt*. Expect renames through the 1.x line.
  • Concurrency is not addressed in the documentation (high, business-critical). Overlap validation (noOverlap(), conflict_detection) appears to be application-level. If there is no transactional lock, unique constraint, or SELECT ... FOR UPDATE around check-and-write, two simultaneous booking requests can both pass validation and double-book. This is the single highest-severity exposure for any booking product and must be verified in source, not assumed.
  • Timezone and DST behavior is undocumented. Schedules store wall-clock periods ('09:00', '17:00') across date ranges spanning a year. Nothing in the README states what timezone periods are interpreted in, whether the schedulable model carries a timezone, or how a recurring 09:00 behaves across a DST boundary. For a global product this is a blocking unknown; for a single-timezone internal tool it is tolerable.
  • No calendar interoperability. No iCal export, no Google/Microsoft sync. If stakeholders expect "the doctor sees their Zap bookings in Outlook," that is a bespoke integration project, not a config flag.

Key questions

  1. What are the exact composer.json constraints for php and illuminate/*, and do they match the README badges or the CI matrix?
  2. Is conflict validation enforced inside a DB transaction with row locking or a unique index? Where?
  3. How are timezones modeled — per schedule, per schedulable model, or app-default? What is the DST behavior for recurring periods?
  4. Are there hard caps on recurrence expansion (a forYear(2025) weekly schedule across 200 resources), and is expansion done in PHP or SQL?
  5. What is the maintainer's stated support window and Laravel-major upgrade cadence?
  6. Is there any intent to support Laravel 11/12, and would a fork relaxing constraints be viable if not?

Integration Approach

Stack fit

Conditional. Zap is a strong fit for Laravel applications that are (a) already on, or imminently moving to, Laravel 13 / PHP 8.5, and (b) have a genuine multi-resource or complex-recurrence requirement. It is a poor fit as a drop-in on a mature Laravel 10/11/12 estate, where the constraint block alone likely prevents composer require. It is also over-engineered for a single-resource, simple-hours booking flow where a starts_at/ends_at table and a unique index would suffice.

Migration path

Treat Zap as a slot-computation service, not a booking system of record, for the first phase.

  1. Constraint verification spike (1 day). Read composer.json; stand up a throwaway PHP 8.5 / Laravel 13 container; run composer require and the package's own test suite. Output: go/no-go on install, and a definitive supported-version matrix.
  2. Shadow-mode integration. Add the trait and publish migrations on a branch. Port one resource type's existing availability rules into Zap schedules. Call getBookableSlots() read-only and diff its output against the current engine over a 90-day window. No writes, no user impact.
  3. Parity gate. The diff must be empty or explained for every day, including month boundaries, DST transitions, and year boundaries. Golden-file fixtures for the ordinal-weekday and N-week recurrence patterns specifically.
  4. Dual-write, single-read. Write appointments to both engines; continue reading slots from legacy. This surfaces concurrency behavior under real traffic before it can hurt customers.
  5. Cut reads over, then cut writes over, then decommission legacy tables.

For a UUID codebase, step 2 must be preceded by the model-extension and migration-editing work from the README, with the config repoint committed before php artisan migrate ever runs.

Compatibility

  • Database: CI matrix covers SQLite, MySQL, and PostgreSQL, and v1.3.0 specifically fixed cross-database time-overlap comparison (PR #29). That is a good signal — time comparison semantics genuinely differ across engines, and the failure mode is silent wrong answers, not exceptions. Still re-verify on our exact engine and version.
  • UUIDs/ULIDs: supported but manual; budget real effort.
  • AI tooling: Laravel Boost 2.0 skills (zap-schedules, zap-availability, zap-recurrence) ship with the package and require no configuration. If the team uses AI-assisted development, this measurably lowers the cost of getting recurrence semantics right and reduces hallucinated method names — worth enabling deliberately rather than incidentally.

Sequencing

Phase Work Gate
0 Constraint spike on PHP 8.5 / Laravel 13 composer require succeeds, package tests pass
1 Schema review; UUID path if applicable Migrations apply cleanly on a clone of prod
2 Single resource, shadow-mode slot computation Parity diff clean over 90 days
3 Concurrency source review + load test Double-booking provably impossible or mitigated
4 Dual-write No divergence under production traffic
5 Read cutover, then write cutover Rollback rehearsed
6 Legacy retirement —

Do not compress phases 2–4. The package's core value is correctness in edge cases, and edge cases are exactly what shadow-mode diffing is for.

Operational Impact

Maintenance

Ongoing cost is low in code and moderate in risk. The package is MIT, self-contained, two tables plus a config file, and does not patch Laravel internals. Upgrades track Laravel majors, which ship annually — so expect an annual compatibility hop, and expect it to be gated on the maintainer publishing first, since a single-maintainer project cannot ship a fix on our timeline. The realistic mitigation is vendoring: fork the package into our VCS at a pinned commit so we can patch a constraint or a bug without waiting for upstream. With 0 dependents and one maintainer, the fork is not optional insurance — it is the plan.

Support

No commercial support tier. Channels are GitHub issues and a docs site (laravel-zap.com); security is a personal mailbox rather than a disclosure policy. For an internal scheduling tool this is acceptable. For a customer-facing booking product, we need an internal owner who can read the source, because there is no escalation path and no SLA.

Scaling

The scaling profile is recurrence expansion, not request volume. getBookableSlots() must materialize candidate periods for a date before subtracting appointments and blocks. A weekly forYear() availability across a date-range query, multiplied by N resources in a listing view, is a plausible hot path. Mitigations: cache computed slot sets per (resource, date), keep queries to single-date granularity rather than wide ranges, and confirm the schedules table is indexed on (schedulable_type, schedulable_id) plus the date bounds — the morphs() helper covers the first two but not the date columns. v1.3.0's query optimization PR (#26) and v1.5.0's buffer-time support indicate the maintainer is watching this, but we should benchmark our own resource cardinality rather than assume.

Failure modes

Failure Impact Detection Mitigation
Concurrent double-booking Customer-facing, high Load test in phase 3 Transactional lock / unique constraint outside the package if absent
Timezone / DST drift Silent wrong slots, seasonal Golden-file tests across DST boundary Confirm tz model; normalize at the boundary
Recurrence anchor errors (biweekly, every-N-weeks) Wrong days offered Parity diff over 90+ days Fixture tests pinning anchor dates
UUID misconfiguration before migrate Schema broken at deploy Staging clone run Enforce documented ordering in the runbook
Laravel major upgrade blocked on upstream Security patching stalls Annual dependency review Maintained fork
Silent empty availability (bad anchor / typo'd period) Users see "no slots", no error Slot-count alerts per resource Instrument: alert when a resource with availability returns zero slots for 3+ days
Overlap-validation bypass via custom type Double booking on custom rules Code review Restrict custom() to reviewed use only

The most dangerous failure mode is the quiet ones — DST drift and anchor errors return plausible-looking slots rather than errors, so they are found by customers, not by monitoring. Instrument slot-count anomalies.

Ramp-up

Authoring API reads well and ramps fast: Zap::for($doctor)->named('Office Hours')->availability()->forYear(2025)->addPeriod('09:00','12:00')->weekly([...])->save() is close to self-documenting for a mid-level Laravel developer. Expect under a day to productive for simple availability, blocks, and appointments.

Recurrence is the real learning curve. Odd/even weeks, weekOddDays/weekEvenDays, biweekly with an anchor date, and everyFourWeeks with a separate anchor argument have subtle, easy-to-invert semantics, and a mistake produces wrong data rather than a crash. Budget for: a curated internal cheat-sheet restricted to the 5 patterns we actually use, a fixture test suite for each of those patterns, and a lint/review rule that new recurrence configurations require a second pair of eyes. The shipped Boost skills help here if the team uses AI-assisted development — point agents at zap-recurrence rather than letting them infer the API.

Recommendation: pursue only if the constraint check in phase 0 passes and the target stack is Laravel 13 / PHP 8.5. Adopt as a slot-computation engine behind a shadow-mode parity gate, fork for supply-chain safety, and verify concurrency handling in source before any customer-facing write path is enabled. Score 6.9 is a fair read: high conceptual value in recurrence, low maturity as a dependency.

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.
oxygen/data
oxygen/core
jengo/base
xiloryx/log-collector
a21ns1g4ts/filament-stripe
la-php/laravel-admin
artisan-build/community-prompts
vleroy/smart
ayles-software/laravel-australian-public-holidays
cboxdk/laravel-id-spatie
cboxdk/laravel-id-billing
cboxdk/laravel-id-billing-bridge
simple-as-fuck/php-performance-log
hsluv/hsluv
seampact/codegen
seampact/hooks
seampact/ir
mehedimi/laravel-amp-mail
happones/laravel-vue-i18n-generator
jwerd/inertia-table