laraveljutsu / zap
A flexible, performant, and developer-friendly schedule management system for Laravel
Installs: 1 609
Dependents: 0
Suggesters: 0
Security: 0
Stars: 718
Watchers: 5
Forks: 39
Open Issues: 4
Requires
- php: ^8.2
Requires (Dev)
- larastan/larastan: ^3.0
- laravel/pint: ^1.20
- orchestra/testbench: ^9.10
- pestphp/pest: ^3.7
README

⚡ Laravel Zap
Lightning-fast schedule management for Laravel
A flexible, performant, and developer-friendly schedule management system with deep Laravel integration.
Installation • Quick Start • Features • Schedule Types • Documentation • Contributing
⚠️ Beta Warning: This package is currently in beta. While we're working hard to ensure stability, it's not recommended for use in production environments yet.
✨ Features
- 🏗️ Eloquent Integration - User HasMany Schedules with period-based scheduling
- ⚡ Business Rules Engine - Configurable validation with Laravel integration
- 🎛️ Granular Rule Control - Individual rule enable/disable with per-schedule overrides
- ⏰ Temporal Operations - Carbon-based date/time manipulation with timezone support
- 🔍 Smart Conflict Detection - Automatic overlap checking with customizable buffers
- 🔄 Recurring Schedules - Support for daily, weekly, monthly, and custom patterns
- 📊 Availability Management - Intelligent time slot generation and conflict resolution
- 🎯 Schedule Types - Availability, appointment, blocked, and custom scheduling types
- 🧩 Laravel Native - Facades, service providers, events, and configuration
- 👩💻 Developer Experience - Fluent API, comprehensive testing, and clear documentation
📋 Requirements
- PHP 8.2+
- Laravel 11.0+
- Carbon 2.0+ or 3.0+
📦 Installation
Install Package
composer require laraveljutsu/zap
Setup
# Publish and run migrations php artisan vendor:publish --tag=zap-migrations php artisan migrate # Publish configuration (optional) php artisan vendor:publish --tag=zap-config
Add Trait to Models
use Zap\Models\Concerns\HasSchedules; class User extends Authenticatable { use HasSchedules; // ... }
🚀 Quick Start
Basic Schedule
use Zap\Facades\Zap; $user = User::find(1); $schedule = Zap::for($user) ->named('Doctor Appointment') ->description('Annual checkup') ->from('2025-03-15') ->addPeriod('09:00', '10:00') ->save();
Recurring Schedule
// Weekly team meeting $meeting = Zap::for($user) ->named('Team Standup') ->from('2025-01-01') ->to('2025-12-31') ->addPeriod('09:00', '09:30') ->weekly(['monday', 'wednesday', 'friday']) ->save();
Schedule with Rules
$schedule = Zap::for($user) ->named('Client Meeting') ->from('2025-03-15') ->addPeriod('14:00', '16:00') ->noOverlap() // Prevent conflicts ->workingHoursOnly('09:00', '18:00') // Business hours only ->maxDuration(240) // Max 4 hours ->withMetadata([ 'location' => 'Conference Room A', 'priority' => 'high' ]) ->save();
Schedule with Rule Overrides
// Override specific rules for this schedule $schedule = Zap::for($user) ->named('Weekend Emergency') ->from('2025-03-16') ->addPeriod('08:00', '20:00') ->withRule('working_hours', ['enabled' => false]) // Allow outside business hours ->withRule('no_weekends', ['enabled' => false]) // Allow weekend scheduling ->withRule('max_duration', ['enabled' => false]) // No duration limits ->save();
🎯 Schedule Types
Laravel Zap supports four distinct schedule types to handle complex scheduling scenarios:
1. Availability Schedules
Working hours or open time slots that allow overlaps. Perfect for defining when someone is available.
// Define working hours $availability = Zap::for($doctor) ->named('Office Hours') ->description('Available for patient appointments') ->availability() ->from('2025-01-01') ->to('2025-12-31') ->addPeriod('09:00', '12:00') // Morning session ->addPeriod('14:00', '17:00') // Afternoon session ->weekly(['monday', 'tuesday', 'wednesday', 'thursday', 'friday']) ->save();
2. Appointment Schedules
Actual bookings that prevent overlaps. These are concrete appointments scheduled within availability windows.
// Create a patient appointment $appointment = Zap::for($doctor) ->named('Patient A - Checkup') ->description('Annual checkup appointment') ->appointment() ->from('2025-01-15') ->addPeriod('10:00', '11:00') ->withMetadata([ 'patient_id' => 1, 'appointment_type' => 'checkup', 'notes' => 'Annual physical examination' ]) ->save();
3. Blocked Schedules
Unavailable time periods that prevent overlaps. Used for lunch breaks, holidays, or maintenance.
// Define lunch break $lunchBreak = Zap::for($doctor) ->named('Lunch Break') ->description('Unavailable for appointments') ->blocked() ->from('2025-01-01') ->to('2025-12-31') ->addPeriod('12:00', '13:00') ->weekly(['monday', 'tuesday', 'wednesday', 'thursday', 'friday']) ->save();
4. Custom Schedules
Default type for backward compatibility. Behavior depends on explicit rules.
// Custom schedule with explicit rules $custom = Zap::for($user) ->named('Custom Event') ->custom() ->from('2025-01-15') ->addPeriod('15:00', '16:00') ->noOverlap() // Explicitly prevent overlaps ->save();
Schedule Type Methods
// Query schedules by type $availability = Schedule::availability()->get(); $appointments = Schedule::appointments()->get(); $blocked = Schedule::blocked()->get(); // Using relationship methods $userAppointments = $user->appointmentSchedules()->get(); $userAvailability = $user->availabilitySchedules()->get(); $userBlocked = $user->blockedSchedules()->get(); // Check schedule type $schedule->isAvailability(); // true/false $schedule->isAppointment(); // true/false $schedule->isBlocked(); // true/false $schedule->isCustom(); // true/false
🔧 Advanced Usage
Availability Checking
// Check availability $available = $user->isAvailableAt('2025-03-15', '14:00', '16:00'); // Get available slots $slots = $user->getAvailableSlots( date: '2025-03-15', dayStart: '09:00', dayEnd: '17:00', slotDuration: 60 ); // Find next available slot $nextSlot = $user->getNextAvailableSlot( afterDate: '2025-03-15', duration: 120, dayStart: '09:00', dayEnd: '17:00' );
Conflict Management
// Check for conflicts $conflicts = Zap::findConflicts($schedule); // Automatic conflict prevention try { $schedule = Zap::for($user) ->from('2025-03-15') ->addPeriod('14:00', '16:00') ->noOverlap() ->save(); } catch (ScheduleConflictException $e) { $conflicts = $e->getConflictingSchedules(); }
Advanced Rule Control
// Disable overlap checking for availability schedules only config(['zap.default_rules.no_overlap.applies_to' => ['appointment', 'blocked']]); // Create availability that can overlap $availability = Zap::for($user) ->named('General Availability') ->availability() // Schedule type: availability ->from('2025-03-15') ->addPeriod('09:00', '17:00') ->save(); // No overlap validation applied // Create appointment that requires validation $appointment = Zap::for($user) ->named('Client Meeting') ->appointment() // Schedule type: appointment ->from('2025-03-15') ->addPeriod('10:00', '11:00') ->save(); // Overlap validation applied // Emergency override for specific case $emergency = Zap::for($user) ->named('Emergency Surgery') ->from('2025-03-15') ->addPeriod('10:30', '12:00') ->withRule('no_overlap', ['enabled' => false]) ->save(); // Bypasses overlap validation
Schedule Queries
// Get schedules for date $todaySchedules = $user->schedulesForDate(today()); // Get schedules for range $weekSchedules = $user->schedulesForDateRange('2025-03-11', '2025-03-17'); // Advanced queries $schedules = Schedule::active() ->forDate('2025-03-15') ->whereHas('periods', function ($query) { $query->whereBetween('start_time', ['09:00', '17:00']); }) ->get();
⚙️ Configuration
Configure Zap in config/zap.php
:
return [ 'default_rules' => [ 'no_overlap' => [ 'enabled' => true, 'applies_to' => [ // Granular control \Zap\Enums\ScheduleTypes::APPOINTMENT, \Zap\Enums\ScheduleTypes::BLOCKED, ], ], 'working_hours' => [ 'enabled' => false, 'start' => '09:00', 'end' => '17:00', 'timezone' => null, // Uses app timezone if null ], 'max_duration' => [ 'enabled' => false, 'minutes' => 480, // 8 hours ], 'no_weekends' => [ 'enabled' => false, 'saturday' => true, 'sunday' => true, ], ], 'conflict_detection' => [ 'enabled' => true, 'buffer_minutes' => 0, // Buffer time between schedules 'auto_resolve' => false, // Automatically resolve conflicts 'strict_mode' => true, // Throw exceptions on conflicts ], 'validation' => [ 'require_future_dates' => true, // Schedules must be in the future 'max_date_range' => 365, // Maximum days between start and end date 'min_period_duration' => 15, // Minimum period duration in minutes 'max_period_duration' => 480, // Maximum period duration in minutes 'max_periods_per_schedule' => 50, // Maximum periods per schedule 'allow_overlapping_periods' => false, // Allow periods to overlap within same schedule ], ];
Individual Rule Control
Control each validation rule independently:
// Disable specific rules config(['zap.default_rules.working_hours.enabled' => false]); config(['zap.default_rules.no_overlap.enabled' => false]); // Granular overlap control - only check overlaps for specific schedule types config(['zap.default_rules.no_overlap.applies_to' => [\Zap\Enums\ScheduleTypes::APPOINTMENT]]); // Allow weekend scheduling config(['zap.default_rules.no_weekends.enabled' => false]);
Per-Schedule Rule Override
Override rules for specific schedules:
// Emergency appointment that can overlap $schedule = Zap::for($user) ->named('Emergency Consultation') ->from('2025-03-15') ->addPeriod('10:00', '11:00') ->withRule('no_overlap', ['enabled' => false]) ->save(); // Weekend work with extended hours $schedule = Zap::for($user) ->named('Weekend Project') ->from('2025-03-16') // Saturday ->addPeriod('08:00', '20:00') ->withRule('working_hours', ['enabled' => false]) ->withRule('no_weekends', ['enabled' => false]) ->save();
🎯 Use Cases
📅 Hospital Scheduling System
// Doctor's working hours (availability) $availability = Zap::for($doctor) ->named('Dr. Smith - Office Hours') ->availability() ->from('2025-01-01') ->to('2025-12-31') ->addPeriod('09:00', '12:00') ->addPeriod('14:00', '17:00') ->weekly(['monday', 'tuesday', 'wednesday', 'thursday', 'friday']) ->save(); // Lunch break (blocked) $lunchBreak = Zap::for($doctor) ->named('Lunch Break') ->blocked() ->from('2025-01-01') ->to('2025-12-31') ->addPeriod('12:00', '13:00') ->weekly(['monday', 'tuesday', 'wednesday', 'thursday', 'friday']) ->save(); // Patient appointments $appointment1 = Zap::for($doctor) ->named('Patient A - Consultation') ->appointment() ->from('2025-01-15') ->addPeriod('10:00', '11:00') ->withMetadata(['patient_id' => 1, 'type' => 'consultation']) ->save(); $appointment2 = Zap::for($doctor) ->named('Patient B - Follow-up') ->appointment() ->from('2025-01-15') ->addPeriod('15:00', '16:00') ->withMetadata(['patient_id' => 2, 'type' => 'follow-up']) ->save();
🏢 Meeting Room Management
// Room availability $roomAvailability = Zap::for($room) ->named('Conference Room A - Available') ->availability() ->from('2025-01-01') ->to('2025-12-31') ->addPeriod('08:00', '18:00') ->weekly(['monday', 'tuesday', 'wednesday', 'thursday', 'friday']) ->save(); // Room maintenance (blocked) $maintenance = Zap::for($room) ->named('Monthly Maintenance') ->blocked() ->from('2025-01-01') ->addPeriod('18:00', '20:00') ->monthly(['day_of_month' => 1]) ->save(); // Meeting bookings $meeting = Zap::for($room) ->named('Board Meeting') ->appointment() ->from('2025-03-15') ->addPeriod('09:00', '11:00') ->withMetadata([ 'organizer' => 'john@company.com', 'equipment' => ['projector', 'whiteboard'] ]) ->save();
👨💼 Employee Shift Management
// Regular shifts (availability) $workSchedule = Zap::for($employee) ->named('Regular Shift') ->availability() ->from('2025-01-01')->to('2025-12-31') ->addPeriod('09:00', '17:00') ->weekly(['monday', 'tuesday', 'wednesday', 'thursday', 'friday']) ->save(); // Actual work assignments (appointments) $assignment = Zap::for($employee) ->named('Project Alpha') ->appointment() ->from('2025-03-15') ->addPeriod('09:00', '12:00') ->withMetadata(['project_id' => 'alpha', 'priority' => 'high']) ->save(); // Time off (blocked) $vacation = Zap::for($employee) ->named('Vacation Leave') ->blocked() ->from('2025-06-01') ->to('2025-06-15') ->addPeriod('00:00', '23:59') ->save();
📡 Events & Testing
Events
// Listen to schedule events protected $listen = [ \Zap\Events\ScheduleCreated::class => [ \App\Listeners\SendScheduleNotification::class, ], ];
Testing Helpers
// Create test schedules easily $schedule = createScheduleFor($user, [ 'name' => 'Test Meeting', 'start_date' => '2025-01-01', 'periods' => [['start_time' => '09:00', 'end_time' => '10:00']], ]);
🛠️ Performance & Optimization
Database Optimization
// Custom indexes for better performance Schema::table('schedules', function (Blueprint $table) { $table->index(['schedulable_type', 'start_date', 'is_active']); });
Eager Loading
// Optimize queries $schedules = Schedule::with(['periods', 'schedulable']) ->forDateRange('2025-03-01', '2025-03-31') ->get();
🤝 Contributing
We welcome contributions!
Development Setup
git clone https://github.com/laraveljutsu/zap.git
cd zap
composer install
vendor/bin/pest
📜 License
Laravel Zap is open-source software licensed under the MIT License.
🔒 Security
If you discover security vulnerabilities, please email ludo@epekta.com instead of using the issue tracker.
⚡ Made with ❤️ by Laravel Jutsu for the Laravel community ⚡