The missing calendar management for Laravel
A flexible, performant, and developer-friendly calendar management system with deep Laravel integration.
Installation • Quick Start • Schedule Types • Features • Documentation • Contributing
Requirements: PHP 8.2+ • Laravel 11.0+ • Carbon 2.0/3.0+
composer require laraveljutsu/zap# Publish and run migrations
php artisan vendor:publish --tag=zap-migrations
php artisan migrate
# Publish configuration (optional)
php artisan vendor:publish --tag=zap-configuse Zap\Models\Concerns\HasSchedules;
class User extends Authenticatable
{
use HasSchedules;
// ...
}use Zap\Facades\Zap;
$schedule = Zap::for($user)
->named('Doctor Appointment')
->description('Annual checkup')
->on('2025-03-15') // on() is an alias of from()
->addPeriod('09:00', '10:00')
->save();// 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();Important
The workingHoursOnly() and maxDuration() methods require enabling working_hours and max_duration validation rules in your config file. These are disabled by default.
$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();Zap for Laravel supports four distinct schedule types for complex scheduling scenarios:
Define when someone/something is available. Allows overlaps.
$availability = Zap::for($doctor)
->named('Office Hours')
->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();Concrete appointments within availability windows. Prevents overlaps.
$appointment = Zap::for($doctor)
->named('Patient A - Checkup')
->appointment()
->from('2025-01-15')
->addPeriod('10:00', '11:00')
->withMetadata(['patient_id' => 1, 'type' => 'checkup'])
->save();Time periods that block scheduling (lunch, holidays). Prevents overlaps.
$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();Default type with explicit rule control.
$custom = Zap::for($user)
->named('Custom Event')
->custom()
->from('2025-01-15')
->addPeriod('15:00', '16:00')
->noOverlap() // Explicitly prevent overlaps
->save();// 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();
// Check schedule type
$schedule->isAvailability(); // true/false
$schedule->isAppointment(); // true/false
$schedule->isBlocked(); // true/false- 🏗️ Eloquent Integration - Native Laravel models and relationships
- 🎛️ Business Rules Engine - Configurable validation with granular control
- ⏰ Temporal Operations - Carbon-based date/time with timezone support
- 🔍 Smart Conflict Detection - Automatic overlap checking with buffers
- 🔄 Recurring Schedules - Daily, weekly, monthly, and custom patterns
- 📊 Availability Management - Intelligent time slot generation
- 🎯 Schedule Types - Availability, appointment, blocked, and custom
- 🧩 Laravel Native - Facades, service providers, events, configuration
- 👩💻 Developer Experience - Fluent API, comprehensive testing, documentation
// 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'
);// 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();
}// 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()
->from('2025-03-15')
->addPeriod('09:00', '17:00')
->save(); // No 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// 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();🏥 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
$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();🏢 Meeting Room Management
// Room availability
$roomAvailability = Zap::for($room)
->named('Conference Room A')
->availability()
->from('2025-01-01')->to('2025-12-31')
->addPeriod('08:00', '18:00')
->weekly(['monday', 'tuesday', 'wednesday', 'thursday', 'friday'])
->save();
// Meeting bookings
$meeting = Zap::for($room)
->named('Board Meeting')
->appointment()
->from('2025-03-15')
->addPeriod('09:00', '11:00')
->withMetadata([
'organizer' => '[email protected]',
'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();
// 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();We welcome contributions! Here's how to get started:
git clone https://github.com/laraveljutsu/zap.git
cd zap
composer install
vendor/bin/pest- Follow PSR-12 coding standards
- Write tests for new features
- Update documentation as needed
Zap for Laravel is open-source software licensed under the MIT License.
git config pull.rebase false If you discover security vulnerabilities, please email [email protected] instead of using the issue tracker.
⚡ Made with ❤️ by Laravel Jutsu for the Laravel community ⚡