laraveljutsu/zap
Zap adds flexible calendar scheduling to Laravel: manage resource availabilities, appointments, blocked times, and custom schedules (doctors, rooms, staff). Includes patterns plus querying/availability checks for booking, shifts, and shared spaces.
isBookableAtTime and slot lengthstartOfWeek-style logic, ensuring consistent week calculations. (#68)Composer:
"laraveljutsu/zap": "^1.15"
Laravel 13.x Compatibility
Recurring schedules can now use “which weekday of the month” (1st, 2nd, 3rd, 4th, or last) instead of only fixed days of the month.
firstWednesdayOfMonth() – every 1st WednesdaysecondFridayOfMonth() – every 2nd FridaylastMondayOfMonth() – every last Mondayfirst{Sunday|Monday|...|Saturday}OfMonth(), second...,
third..., fourth..., last...Zap::for($room) ->named('Monthly Standup') ->firstWednesdayOfMonth() ->forYear(2025) ->addPeriod('09:00', '10:00') ->save();
zap-recurrence and zap-schedules updated with ordinal weekday docs.No breaking changes. Safe to upgrade from v1.12.x
Recurring schedules can now use custom week and month intervals beyond the built-in weekly, biweekly, monthly, quarterly, and annual patterns.
Every X weeks (3–52):
everyThreeWeeks(), everyFourWeeks(), everyFiveWeeks(), … through everyFiftyTwoWeeks().everyXWeeks(array $days, CarbonInterface|string|null $startsOn = null).startsOn anchor date to fix the first occurrence week.Zap::for($resource)
->named('Tri-Weekly Sync')
->everyThreeWeeks(['monday', 'friday'])
->from('2025-01-06')
->to('2025-12-31')
->addPeriod('10:00', '11:00')
->save();
Every X months (4, 5, 7–11):
everyFourMonths(), everyFiveMonths(), everySevenMonths(), everyEightMonths(), everyNineMonths(), everyTenMonths(), everyElevenMonths().day_of_month or days_of_month, optional start_month anchor.Zap::for($resource)
->named('Quadrimester Review')
->everyFourMonths(['day_of_month' => 15])
->forYear(2025)
->addPeriod('09:00', '12:00')
->save();
forDate() scope and non-recurring schedules with NULL end_dateforDate() scope on the Schedule model now correctly includes non-recurring schedules when end_date is NULL (open-ended single-day or ongoing schedules).start_date.ext-intl PHP extension as a requirement (used for recurrence calculations).WeeklyFrequencyConfig\ and MonthlyFrequencyConfig\ namespaces with shared abstract classes for weekly and monthly frequencies.everyXWeeks and everyXMonths examples.php -m | grep intl.composer update laraveljutsu/zap to pull 1.12.0.weekly(), biweekly(), monthly(), etc. behave as before.Thanks to seebaermichi and Sagmedjo for their pull requests in this release.
php artisan vendor:publish --tag=zap-migrationsFull Changelog: https://github.com/ludoguenet/laravel-zap/compare/v1.10.0...v1.11.0
isBookableAtTime() MethodAdded a new method to check if a specific time range is bookable for a given date:
$isBookable = $doctor->isBookableAtTime('2025-01-15', '9:00', '9:30');
This method checks two conditions:
Full support for custom models using UUIDs, ULIDs, GUIDs, or any other primary key type:
Zap\Models\Schedule and Zap\Models\SchedulePeriod in your own namespaceconfig/zap.phpEnhanced weekly odd/even scheduling logic with better date handling and comprehensive test coverage.
scopeForDateRange to properly handle null end_date valuesgetMorphClass() for proper type resolutionNo breaking changes. This is a backward-compatible release. Simply update:
composer update laraveljutsu/zap
Full Changelog: https://github.com/ludoguenet/laravel-zap/compare/v1.9.2...v1.10.0
• Committed the workflow change: "ci: add PHP 8.5 to test matrix" • Created annotated tag: v1.9.0 with message "Release 1.9.0: PHP 8.5 support" • Pushed tag to remote: v1.9.0 is now on GitHub • Pushed commit to main: workflow update is on the main branch
The release tag is live. The CI workflow will test against PHP 8.2, 8.3, 8.4, and 8.5 across SQLite, MySQL, and PostgreSQL. The deprecation warning from Composer's dependencies is harmless and doesn't affect the release.
Release 1.8.0 focuses on API clarity, enhanced testing, and comprehensive documentation updates.
getAvailableSlots() and getNextAvailableSlot() have been replaced with getBookableSlots() and getNextBookableSlot()getBookableSlots() now automatically detects availability periods from schedules, removing the need for manual dayStart and dayEnd parametersSlotsEdgeCasesTest.php and BufferTimeAvailabilityTest.phpZapTestUser, ZapTestRoom) and migrationsbuffer-time.md, schedule-types.md) with more detailed explanationsThis release maintains backward compatibility while providing a clearer, more intuitive API for finding and booking time slots in Laravel applications.
forYear() Convenience Method
start_date to January 1st and end_date to December 31st of the specified year$schedule = Zap::for($user)
->named('Annual Availability')
->forYear(2025) // Sets start_date to 2025-01-01 and end_date to 2025-12-31
->addPeriod('09:00', '17:00')
->weekly(['monday', 'tuesday', 'wednesday', 'thursday', 'friday'])
->save();
forDate() scope to properly filter weekly schedules by weekdayforDate() scope to properly filter monthly schedules by day of monthisAvailableAt() incorrectly returning false for time slots after a schedule's end dateforYear() method instead)forYear() methodforYear() builder methodFull Changelog: v1.6.0...v1.7.0
$bufferTimeMinutes support to getAvailableSlots()getBookableSlots() methodThanks to @lukacavic, @angelej, and @ThomasDeer for the feedback!
🚀 Release v1.4.0
Merged Pull Requests
#33 — Improve code quality and fix minor issues
#32 — Enhance test coverage and refactor configuration handling
🔧 Maintenance
Ran composer qa (Laravel Pint + tests)
Updated dependencies via composer update
✅ All checks passed successfully. 🧹 Codebase cleaned and up to date.
✨ Query optimizations
getAvailableSlots query performance.🐘🐬 Cross-database time comparison fixes
🧹 Misc
Big thanks to the community contributors! 💙
This release is a rebranding / renaming update:
Zap is a calendar and event management tool designed for Laravel applications.
⚖️ Note: This project is independent and not affiliated with Laravel LLC or Taylor Otwell. "Laravel" is used only to indicate compatibility.
scopeOverlapping method with proper driver detection🏷️ Tag Details Tag Name: v1.1.1 Type: Annotated tag (includes metadata and message) Message: Describes the documentation improvements and fixes
📋 What's Included in v1.1.1 ✨ Schedule Types Documentation: Comprehensive guide for availability, appointment, blocked, and custom types 🔧 Configuration Fixes: Updated config examples to match actual implementation 📝 Use Case Updates: Enhanced examples using proper schedule types 🧹 Cleanup: Removed non-existent features (like caching) from documentation
Based on your questions, we've just released a comprehensive Schedule Types feature that addresses all your concerns:
Q1: "Is using the metadata field the recommended way to differentiate between schedule types?"
schedule_type column with proper enum values.Q2: "Would it make more sense to add a dedicated schedule_type column for performance and clarity?"
Q3: "Why doesn't Zap include schedule_type in the base schema?"
availability - Working hours/open slots (allows overlaps)appointment - Actual bookings (prevents overlaps)blocked - Unavailable periods (prevents overlaps)custom - Backward compatible default (respects noOverlap() rules)// Hospital scheduling made simple
$doctor = User::find(1);
// Set doctor's availability (allows overlaps)
Zap::for($doctor)
->availability()
->from('2025-01-01')
->addPeriod('09:00', '17:00')
->save();
// Book an appointment (prevents overlaps)
Zap::for($doctor)
->appointment()
->from('2025-01-01')
->addPeriod('10:00', '11:00')
->save();
// Block lunch break (prevents overlaps)
Zap::for($doctor)
->blocked()
->from('2025-01-01')
->addPeriod('12:00', '13:00')
->save();
noOverlap() behaviorschedule_type column for fast queries// Get all appointments for a doctor
$appointments = Schedule::for($doctor)->appointments()->get();
// Get availability schedules
$availability = Schedule::for($doctor)->availability()->get();
// Query by specific type
$blocked = Schedule::for($doctor)->ofType('blocked')->get();
Don't worry about existing code! We've maintained 100% backward compatibility:
'custom' typenoOverlap() rules still function as before// Perfect for hospital scheduling
$doctor = User::find(1);
// 1. Set working hours (availability)
Zap::for($doctor)->availability()
->weekly(['monday', 'tuesday', 'wednesday'])
->addPeriod('09:00', '17:00')
->save();
// 2. Book patient appointment (appointment)
Zap::for($doctor)->appointment()
->from('2025-01-01')
->addPeriod('10:00', '11:00')
->save();
// 3. Block lunch break (blocked)
Zap::for($doctor)->blocked()
->daily()
->addPeriod('12:00', '13:00')
->save();
// 4. Check availability (only considers blocking schedules)
$isAvailable = $doctor->isAvailableAt('2025-01-01 14:00'); // true
$isAvailable = $doctor->isAvailableAt('2025-01-01 10:30'); // false (appointment)
Your questions directly led to this feature implementation. This is exactly the kind of real-world feedback that makes open source better!
Want to try it? Update to the latest version and check out the Schedule Types Documentation for complete examples.
P.S. - Laravel Zap just hit 700+ stars! 🌟 Thank you for being part of our growing community!
How can I help you explore Laravel packages today?