44
55** Flexible schedule management for modern Laravel applications**
66
7- [ ![ PHP Version] ( https://img.shields.io/badge/PHP-%E2%89%A48.5-777BB4?style=flat&logo=php )] ( https://www.php.net/ )
8- [ ![ Laravel Version] ( https://img.shields.io/badge/Laravel-%E2%89%A412.0-FF2D20?style=flat&logo=laravel )] ( https://laravel.com )
9- [ ![ License] ( http://poser.pugx.org/laraveljutsu/zap/license )] ( https://packagist.org/packages/laraveljutsu/zap )
10- [ ![ Total Downloads] ( http://poser.pugx.org/laraveljutsu/zap/downloads )] ( https://packagist.org/packages/laraveljutsu/zap )
7+ [ ![ PHP Version] ( https://img.shields.io/badge/PHP-%E2%89%A48.5-777BB4?style=for-the-badge&logo=php )] ( https://www.php.net/ )
8+ [ ![ Laravel Version] ( https://img.shields.io/badge/Laravel-%E2%89%A412.0-FF2D20?style=for-the-badge&logo=laravel )] ( https://laravel.com )
9+ [ ![ License] ( http://poser.pugx.org/laraveljutsu/zap/license?style=for-the-badge )] ( https://packagist.org/packages/laraveljutsu/zap )
10+ [ ![ Total Downloads] ( http://poser.pugx.org/laraveljutsu/zap/downloads?style=for-the-badge )] ( https://packagist.org/packages/laraveljutsu/zap )
11+ [ ![ Why PHP] ( https://img.shields.io/badge/Why_PHP-in_2026-7A86E8?style=for-the-badge&labelColor=18181b )] ( https://whyphp.dev )
1112
1213[ Website
] ( https://ludovicguenet.dev ) •
[ Documentation
] ( https://laravel-zap.com ) •
[ Support
] ( mailto:[email protected] ) 1314
1718
1819## 🎯 What is Zap?
1920
20- A comprehensive calendar and scheduling system for Laravel. Manage availabilities, appointments, blocked times, and custom schedules for any resource—doctors, meeting rooms, employees, and more.
21+ Zap is a comprehensive calendar and scheduling system for Laravel. Manage availabilities, appointments, blocked times, and custom schedules for any resource—doctors, meeting rooms, employees, and more.
2122
22- ** Perfect for:** appointment booking systems • resource scheduling • shift management • calendar applications
23+ ** Perfect for:**
24+ - 📅 Appointment booking systems
25+ - 🏥 Healthcare resource management
26+ - 👔 Employee shift scheduling
27+ - 🏢 Shared office space bookings
2328
2429---
2530
@@ -33,7 +38,9 @@ php artisan vendor:publish --tag=zap-migrations
3338php artisan migrate
3439```
3540
36- Add the trait to your schedulable models:
41+ ### Setup Your Models
42+
43+ Add the ` HasSchedules ` trait to any Eloquent model you want to make schedulable:
3744
3845``` php
3946use Zap\Models\Concerns\HasSchedules;
@@ -48,6 +55,8 @@ class Doctor extends Model
4855
4956## 🧩 Core Concepts
5057
58+ Zap uses four schedule types to model different scenarios:
59+
5160| Type | Purpose | Overlap Behavior |
5261| ------| ---------| ------------------|
5362| ** Availability** | Define when resources can be booked | ✅ Allows overlaps |
@@ -59,6 +68,8 @@ class Doctor extends Model
5968
6069## 🚀 Quick Start
6170
71+ Here's a complete example of setting up a doctor's schedule:
72+
6273``` php
6374use Zap\Facades\Zap;
6475
@@ -104,7 +115,9 @@ $nextSlot = $doctor->getNextBookableSlot('2025-01-15', 60, 15);
104115
105116## 📅 Schedule Patterns
106117
107- ### Recurrence
118+ ### Recurrence Patterns
119+
120+ Zap supports various recurrence patterns for flexible scheduling:
108121
109122``` php
110123// Daily
@@ -113,20 +126,20 @@ $schedule->daily()->from('2025-01-01')->to('2025-12-31');
113126// Weekly (specific days)
114127$schedule->weekly(['monday', 'wednesday', 'friday'])->forYear(2025);
115128
116- // Weekly with time period (convenience method - combines weekly() and addPeriod() )
129+ // Weekly with time period (convenience method)
117130$schedule->weekDays(['monday', 'wednesday', 'friday'], '09:00', '17:00')->forYear(2025);
118131
119- // Weekly odd (specific days) – runs only on odd-numbered weeks
132+ // Weekly odd (runs only on odd-numbered weeks)
120133$schedule->weeklyOdd(['monday', 'wednesday', 'friday'])->forYear(2025);
121134
122- // Weekly odd with time period (convenience method - combines weeklyOdd() and addPeriod() )
123- $schedule->weekOddDays(['monday', 'wednesday', 'friday'], '09:00', '17:00')->forYear(2025)
135+ // Weekly odd with time period (convenience method)
136+ $schedule->weekOddDays(['monday', 'wednesday', 'friday'], '09:00', '17:00')->forYear(2025);
124137
125- // Weekly even (specific days) – runs only on even-numbered weeks
138+ // Weekly even (runs only on even-numbered weeks)
126139$schedule->weeklyEven(['monday', 'wednesday', 'friday'])->forYear(2025);
127140
128- // Weekly even with time period (convenience method - combines weeklyEven() and addPeriod() )
129- $schedule->weekEvenDays(['monday', 'wednesday', 'friday'], '09:00', '17:00')->forYear(2025)
141+ // Weekly even with time period (convenience method)
142+ $schedule->weekEvenDays(['monday', 'wednesday', 'friday'], '09:00', '17:00')->forYear(2025);
130143
131144// Bi-weekly (week of the start date by default, optional anchor)
132145$schedule->biweekly(['tuesday', 'thursday'])->from('2025-01-07')->to('2025-03-31');
@@ -135,30 +148,38 @@ $schedule->biweekly(['tuesday', 'thursday'])->from('2025-01-07')->to('2025-03-31
135148$schedule->monthly(['days_of_month' => [1, 15]])->forYear(2025);
136149
137150// Bi-monthly (multiple days, optional start_month anchor)
138- $schedule->bimonthly(['days_of_month' => [5, 20], 'start_month' => 2])->from('2025-01-05')->to('2025-06-30');
151+ $schedule->bimonthly(['days_of_month' => [5, 20], 'start_month' => 2])
152+ ->from('2025-01-05')->to('2025-06-30');
139153
140154// Quarterly (multiple days, optional start_month anchor)
141- $schedule->quarterly(['days_of_month' => [7, 21], 'start_month' => 2])->from('2025-02-15')->to('2025-11-15');
155+ $schedule->quarterly(['days_of_month' => [7, 21], 'start_month' => 2])
156+ ->from('2025-02-15')->to('2025-11-15');
142157
143158// Semi-annually (multiple days, optional start_month anchor)
144- $schedule->semiannually(['days_of_month' => [10], 'start_month' => 3])->from('2025-03-10')->to('2025-12-10');
159+ $schedule->semiannually(['days_of_month' => [10], 'start_month' => 3])
160+ ->from('2025-03-10')->to('2025-12-10');
145161
146162// Annually (multiple days, optional start_month anchor)
147- $schedule->annually(['days_of_month' => [1, 15], 'start_month' => 4])->from('2025-04-01')->to('2026-04-01');
163+ $schedule->annually(['days_of_month' => [1, 15], 'start_month' => 4])
164+ ->from('2025-04-01')->to('2026-04-01');
148165```
149166
150167### Date Ranges
151168
169+ Specify when schedules are active:
170+
152171``` php
153172$schedule->from('2025-01-15'); // Single date
154- $schedule->on('2025-01-15'); // Alias (alternative syntax) for from()
173+ $schedule->on('2025-01-15'); // Alias for from()
155174$schedule->from('2025-01-01')->to('2025-12-31'); // Date range
156175$schedule->between('2025-01-01', '2025-12-31'); // Alternative syntax
157176$schedule->forYear(2025); // Entire year shortcut
158177```
159178
160179### Time Periods
161180
181+ Define working hours and time slots:
182+
162183``` php
163184// Single period
164185$schedule->addPeriod('09:00', '17:00');
@@ -170,7 +191,9 @@ $schedule->addPeriod('14:00', '17:00');
170191
171192---
172193
173- ## 🔍 Query & Check
194+ ## 🔍 Query & Check Availability
195+
196+ Check availability and query schedules:
174197
175198``` php
176199// Check if there is at least one bookable slot on the day
@@ -201,7 +224,7 @@ $schedule->isAppointment();
201224$schedule->isBlocked();
202225```
203226
204- > ` isAvailableAt() ` is deprecated in favor of ` isBookableAt() ` ` isBookableAtTime() ` and ` getBookableSlots() ` . Use the bookable APIs for all new code.
227+ > ⚠️ ** Note: ** ` isAvailableAt() ` is deprecated in favor of ` isBookableAt() ` , ` isBookableAtTime() ` , and ` getBookableSlots() ` . Use the bookable APIs for all new code.
205228
206229---
207230
@@ -211,19 +234,32 @@ $schedule->isBlocked();
211234
212235``` php
213236// Office hours
214- Zap::for($doctor)->named('Office Hours')->availability()
215- ->forYear(2025)->weekly(['monday', 'tuesday', 'wednesday', 'thursday', 'friday'])
216- ->addPeriod('09:00', '12:00')->addPeriod('14:00', '17:00')->save();
237+ Zap::for($doctor)
238+ ->named('Office Hours')
239+ ->availability()
240+ ->forYear(2025)
241+ ->weekly(['monday', 'tuesday', 'wednesday', 'thursday', 'friday'])
242+ ->addPeriod('09:00', '12:00')
243+ ->addPeriod('14:00', '17:00')
244+ ->save();
217245
218246// Lunch break
219- Zap::for($doctor)->named('Lunch Break')->blocked()
220- ->forYear(2025)->weekly(['monday', 'tuesday', 'wednesday', 'thursday', 'friday'])
221- ->addPeriod('12:00', '13:00')->save();
247+ Zap::for($doctor)
248+ ->named('Lunch Break')
249+ ->blocked()
250+ ->forYear(2025)
251+ ->weekly(['monday', 'tuesday', 'wednesday', 'thursday', 'friday'])
252+ ->addPeriod('12:00', '13:00')
253+ ->save();
222254
223255// Book appointment
224- Zap::for($doctor)->named('Patient A - Checkup')->appointment()
225- ->from('2025-01-15')->addPeriod('10:00', '11:00')
226- ->withMetadata(['patient_id' => 1])->save();
256+ Zap::for($doctor)
257+ ->named('Patient A - Checkup')
258+ ->appointment()
259+ ->from('2025-01-15')
260+ ->addPeriod('10:00', '11:00')
261+ ->withMetadata(['patient_id' => 1])
262+ ->save();
227263
228264// Get available slots
229265$slots = $doctor->getBookableSlots('2025-01-15', 60, 15);
@@ -233,37 +269,48 @@ $slots = $doctor->getBookableSlots('2025-01-15', 60, 15);
233269
234270``` php
235271// Room availability (using weekDays convenience method)
236- Zap::for($room)->named('Conference Room A')->availability()
272+ Zap::for($room)
273+ ->named('Conference Room A')
274+ ->availability()
237275 ->weekDays(['monday', 'tuesday', 'wednesday', 'thursday', 'friday'], '08:00', '18:00')
238276 ->forYear(2025)
239277 ->save();
240278
241279// Book meeting
242- Zap::for($room)->named('Board Meeting')->appointment()
243- ->from('2025-03-15')->addPeriod('09:00', '11:00')
244- ->withMetadata(['organizer' => '
[email protected] '])->save();
280+ Zap::for($room)
281+ ->named('Board Meeting')
282+ ->appointment()
283+ ->from('2025-03-15')
284+ ->addPeriod('09:00', '11:00')
285+ ->withMetadata(['organizer' => '
[email protected] '])
286+ ->save();
245287```
246288
247289### 👔 Employee Shift Management
248290
249291``` php
250292// Regular schedule (using weekDays convenience method)
251- Zap::for($employee)->named('Regular Shift')->availability()
293+ Zap::for($employee)
294+ ->named('Regular Shift')
295+ ->availability()
252296 ->weekDays(['monday', 'tuesday', 'wednesday', 'thursday', 'friday'], '09:00', '17:00')
253297 ->forYear(2025)
254298 ->save();
255299
256300// Vacation
257- Zap::for($employee)->named('Vacation Leave')->blocked()
301+ Zap::for($employee)
302+ ->named('Vacation Leave')
303+ ->blocked()
258304 ->between('2025-06-01', '2025-06-15')
259- ->addPeriod('00:00', '23:59')->save();
305+ ->addPeriod('00:00', '23:59')
306+ ->save();
260307```
261308
262309---
263310
264311## ⚙️ Configuration
265312
266- Publish and customize:
313+ Publish and customize the configuration :
267314
268315``` bash
269316php artisan vendor:publish --tag=zap-config
@@ -290,15 +337,22 @@ Key settings in `config/zap.php`:
290337
291338### Custom Schedules with Explicit Rules
292339
340+ Create custom schedules with explicit overlap rules:
341+
293342``` php
294- Zap::for($user)->named('Custom Event')->custom()
295- ->from('2025-01-15')->addPeriod('15:00', '16:00')
343+ Zap::for($user)
344+ ->named('Custom Event')
345+ ->custom()
346+ ->from('2025-01-15')
347+ ->addPeriod('15:00', '16:00')
296348 ->noOverlap() // Explicitly prevent overlaps
297349 ->save();
298350```
299351
300352### Metadata Support
301353
354+ Attach custom metadata to schedules:
355+
302356``` php
303357->withMetadata([
304358 'patient_id' => 1,
0 commit comments