Skip to content

ludoguenet/laravel-zap

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Zap Logo

Flexible schedule management for modern Laravel applications

PHP Version Laravel Version License Total Downloads Why PHP

Website β€’ Documentation β€’ Support


🎯 What is Zap?

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.

Perfect for:

  • πŸ“… Appointment booking systems
  • πŸ₯ Healthcare resource management
  • πŸ‘” Employee shift scheduling
  • 🏒 Shared office space bookings

πŸ“¦ Installation

Requirements: PHP ≀8.5 β€’ Laravel ≀12.0

composer require laraveljutsu/zap php artisan vendor:publish --tag=zap-migrations php artisan migrate

Setup Your Models

Add the HasSchedules trait to any Eloquent model you want to make schedulable:

use Zap\Models\Concerns\HasSchedules; class Doctor extends Model { use HasSchedules; }

🧩 Core Concepts

Zap uses four schedule types to model different scenarios:

Type Purpose Overlap Behavior
Availability Define when resources can be booked βœ… Allows overlaps
Appointment Actual bookings or scheduled events ❌ Prevents overlaps
Blocked Periods where booking is forbidden ❌ Prevents overlaps
Custom Neutral schedules with explicit rules βš™οΈ You define the rules

πŸš€ Quick Start

Here's a complete example of setting up a doctor's schedule:

use Zap\Facades\Zap; // 1️⃣ Define working hours Zap::for($doctor) ->named('Office Hours') ->availability() ->forYear(2025) ->addPeriod('09:00', '12:00') ->addPeriod('14:00', '17:00') ->weekly(['monday', 'tuesday', 'wednesday', 'thursday', 'friday']) ->save(); // 2️⃣ Block lunch break Zap::for($doctor) ->named('Lunch Break') ->blocked() ->forYear(2025) ->addPeriod('12:00', '13:00') ->weekly(['monday', 'tuesday', 'wednesday', 'thursday', 'friday']) ->save(); // 3️⃣ Create an appointment Zap::for($doctor) ->named('Patient A - Consultation') ->appointment() ->from('2025-01-15') ->addPeriod('10:00', '11:00') ->withMetadata(['patient_id' => 1, 'type' => 'consultation']) ->save(); // 4️⃣ Get bookable slots (60 min slots, 15 min buffer) $slots = $doctor->getBookableSlots('2025-01-15', 60, 15); // Returns: [['start_time' => '09:00', 'end_time' => '10:00', 'is_available' => true, ...], ...] // 5️⃣ Find next available slot $nextSlot = $doctor->getNextBookableSlot('2025-01-15', 60, 15);

πŸ’‘ Tip: You can also use the zap() helper function instead of the facade: zap()->for($doctor)->... (no import needed)


πŸ“… Schedule Patterns

Recurrence Patterns

Zap supports various recurrence patterns for flexible scheduling:

// Daily $schedule->daily()->from('2025-01-01')->to('2025-12-31'); // Weekly (specific days) $schedule->weekly(['monday', 'wednesday', 'friday'])->forYear(2025); // Weekly with time period (convenience method) $schedule->weekDays(['monday', 'wednesday', 'friday'], '09:00', '17:00')->forYear(2025); // Weekly odd (runs only on odd-numbered weeks) $schedule->weeklyOdd(['monday', 'wednesday', 'friday'])->forYear(2025); // Weekly odd with time period (convenience method) $schedule->weekOddDays(['monday', 'wednesday', 'friday'], '09:00', '17:00')->forYear(2025); // Weekly even (runs only on even-numbered weeks) $schedule->weeklyEven(['monday', 'wednesday', 'friday'])->forYear(2025); // Weekly even with time period (convenience method) $schedule->weekEvenDays(['monday', 'wednesday', 'friday'], '09:00', '17:00')->forYear(2025); // Bi-weekly (week of the start date by default, optional anchor) $schedule->biweekly(['tuesday', 'thursday'])->from('2025-01-07')->to('2025-03-31'); // Monthly (supports multiple days) $schedule->monthly(['days_of_month' => [1, 15]])->forYear(2025); // Bi-monthly (multiple days, optional start_month anchor) $schedule->bimonthly(['days_of_month' => [5, 20], 'start_month' => 2]) ->from('2025-01-05')->to('2025-06-30'); // Quarterly (multiple days, optional start_month anchor) $schedule->quarterly(['days_of_month' => [7, 21], 'start_month' => 2]) ->from('2025-02-15')->to('2025-11-15'); // Semi-annually (multiple days, optional start_month anchor) $schedule->semiannually(['days_of_month' => [10], 'start_month' => 3]) ->from('2025-03-10')->to('2025-12-10'); // Annually (multiple days, optional start_month anchor) $schedule->annually(['days_of_month' => [1, 15], 'start_month' => 4]) ->from('2025-04-01')->to('2026-04-01');

Date Ranges

Specify when schedules are active:

$schedule->from('2025-01-15'); // Single date $schedule->on('2025-01-15'); // Alias for from() $schedule->from('2025-01-01')->to('2025-12-31'); // Date range $schedule->between('2025-01-01', '2025-12-31'); // Alternative syntax $schedule->forYear(2025); // Entire year shortcut

Time Periods

Define working hours and time slots:

// Single period $schedule->addPeriod('09:00', '17:00'); // Multiple periods (split shifts) $schedule->addPeriod('09:00', '12:00'); $schedule->addPeriod('14:00', '17:00');

πŸ” Query & Check Availability

Check availability and query schedules:

// Check if there is at least one bookable slot on the day $isBookable = $doctor->isBookableAt('2025-01-15', 60); // Check if a specific time range is bookable $isBookable = $doctor->isBookableAtTime('2025-01-15', '9:00', '9:30'); // Get bookable slots $slots = $doctor->getBookableSlots('2025-01-15', 60, 15); // Find conflicts $conflicts = Zap::findConflicts($schedule); $hasConflicts = Zap::hasConflicts($schedule); // Query schedules $doctor->schedulesForDate('2025-01-15')->get(); $doctor->schedulesForDateRange('2025-01-01', '2025-01-31')->get(); // Filter by type $doctor->appointmentSchedules()->get(); $doctor->availabilitySchedules()->get(); $doctor->blockedSchedules()->get(); // Check schedule type $schedule->isAvailability(); $schedule->isAppointment(); $schedule->isBlocked();

⚠️ Note: isAvailableAt() is deprecated in favor of isBookableAt(), isBookableAtTime(), and getBookableSlots(). Use the bookable APIs for all new code.


πŸ’Ό Real-World Examples

πŸ₯ Doctor Appointment System

// Office hours Zap::for($doctor) ->named('Office Hours') ->availability() ->forYear(2025) ->weekly(['monday', 'tuesday', 'wednesday', 'thursday', 'friday']) ->addPeriod('09:00', '12:00') ->addPeriod('14:00', '17:00') ->save(); // Lunch break Zap::for($doctor) ->named('Lunch Break') ->blocked() ->forYear(2025) ->weekly(['monday', 'tuesday', 'wednesday', 'thursday', 'friday']) ->addPeriod('12:00', '13:00') ->save(); // Book appointment Zap::for($doctor) ->named('Patient A - Checkup') ->appointment() ->from('2025-01-15') ->addPeriod('10:00', '11:00') ->withMetadata(['patient_id' => 1]) ->save(); // Get available slots $slots = $doctor->getBookableSlots('2025-01-15', 60, 15);

🏒 Meeting Room Booking

// Room availability (using weekDays convenience method) Zap::for($room) ->named('Conference Room A') ->availability() ->weekDays(['monday', 'tuesday', 'wednesday', 'thursday', 'friday'], '08:00', '18:00') ->forYear(2025) ->save(); // Book meeting Zap::for($room) ->named('Board Meeting') ->appointment() ->from('2025-03-15') ->addPeriod('09:00', '11:00') ->withMetadata(['organizer' => 'john@company.com']) ->save();

πŸ‘” Employee Shift Management

// Regular schedule (using weekDays convenience method) Zap::for($employee) ->named('Regular Shift') ->availability() ->weekDays(['monday', 'tuesday', 'wednesday', 'thursday', 'friday'], '09:00', '17:00') ->forYear(2025) ->save(); // Vacation Zap::for($employee) ->named('Vacation Leave') ->blocked() ->between('2025-06-01', '2025-06-15') ->addPeriod('00:00', '23:59') ->save();

βš™οΈ Configuration

Publish and customize the configuration:

php artisan vendor:publish --tag=zap-config

Key settings in config/zap.php:

'time_slots' => [ 'buffer_minutes' => 0, // Default buffer between slots ], 'default_rules' => [ 'no_overlap' => [ 'enabled' => true, 'applies_to' => ['appointment', 'blocked'], ], ],

πŸ›‘οΈ Advanced Features

Custom Schedules with Explicit Rules

Create custom schedules with explicit overlap rules:

Zap::for($user) ->named('Custom Event') ->custom() ->from('2025-01-15') ->addPeriod('15:00', '16:00') ->noOverlap() // Explicitly prevent overlaps ->save();

Metadata Support

Attach custom metadata to schedules:

->withMetadata([ 'patient_id' => 1, 'type' => 'consultation', 'notes' => 'Follow-up required' ])

🀝 Contributing

We welcome contributions! Follow PSR-12 coding standards and include tests.

git clone https://github.com/laraveljutsu/zap.git cd zap composer install vendor/bin/pest

πŸ“„ License

Open-source software licensed under the MIT License.

πŸ”’ Security

Report vulnerabilities to ludo@epekta.com (please don't use the issue tracker).


Made with πŸ’› by Ludovic GuΓ©net for the Laravel community