laraveljutsu/zap
Zap is a Laravel scheduling package to manage availabilities, appointments, blocked times, and custom schedules for any resource (doctors, rooms, employees). Query availability, prevent overlaps, and build booking, shift, or shared space workflows.
Installation
composer require laraveljutsu/zap
php artisan vendor:publish --provider="Zap\ZapServiceProvider"
php artisan migrate
Make a Model Schedulable
Add the HasSchedules trait to your model (e.g., Doctor, Room):
use Zap\Models\Concerns\HasSchedules;
class Doctor extends Model
{
use HasSchedules;
}
First Use Case: Define Working Hours
use Zap\Facades\Zap;
Zap::for($doctor)
->named('Office Hours')
->availability()
->forYear(2025)
->addPeriod('09:00', '12:00')
->weekly(['monday', 'tuesday', 'wednesday', 'thursday', 'friday'])
->save();
Check Availability
$slots = $doctor->getBookableSlots('2025-01-15', 60, 15); // 60-min slots, 15-min buffer
availability(), blocked(), and appointment() methods in the Quick Start section.Zap::for($resource)
->named('Working Hours')
->availability()
->weekly(['monday', 'friday'], '09:00', '17:00')
->forYear(2025)
->save();
Zap::for($resource)
->named('Lunch Break')
->blocked()
->addPeriod('12:00', '13:00')
->weekly(['monday', 'tuesday', 'wednesday', 'thursday', 'friday'])
->save();
Zap::for($resource)
->named('Patient Consultation')
->appointment()
->from('2025-01-15')
->addPeriod('10:00', '11:00')
->withMetadata(['patient_id' => 1])
->save();
$isBookable = $doctor->isBookableAt('2025-01-15', 60); // 60-min slot
$isBookableAtTime = $doctor->isBookableAtTime('2025-01-15', '09:00', '09:30');
$slots = $doctor->getBookableSlots('2025-01-15', 30, 10); // 30-min slots, 10-min buffer
$nextSlot = $doctor->getNextBookableSlot('2025-01-15', 30, 10);
Use recurrence patterns for repeating schedules:
// Weekly on specific days
Zap::for($resource)
->named('Weekly Meeting')
->availability()
->weekly(['monday', 'wednesday', 'friday'])
->forYear(2025)
->addPeriod('10:00', '11:00')
->save();
// Monthly on the 1st and 15th
Zap::for($resource)
->named('Monthly Tasks')
->availability()
->monthly(['days_of_month' => [1, 15]])
->forYear(2025)
->addPeriod('09:00', '12:00')
->save();
// Ordinal weekday (e.g., 2nd Friday of the month)
Zap::for($resource)
->named('Bi-weekly Report')
->secondFridayOfMonth()
->forYear(2025)
->addPeriod('14:00', '15:00')
->save();
Validate schedules before saving to avoid overlaps:
$schedule = Zap::for($doctor)
->named('New Appointment')
->appointment()
->from('2025-01-15')
->addPeriod('10:00', '11:00');
if (Zap::hasConflicts($schedule)) {
// Handle conflict (e.g., notify user or adjust time)
} else {
$schedule->save();
}
Attach metadata to schedules and enforce custom rules:
Zap::for($resource)
->named('Custom Event')
->custom()
->from('2025-01-15')
->addPeriod('15:00', '16:00')
->noOverlap() // Prevent overlaps with other schedules
->maxDuration(120) // Max 2-hour duration
->withMetadata(['event_type' => 'workshop', 'attendees' => 5])
->save();
Laravel Events: Trigger events for schedule creation/modification/deletion:
// In your Zap schedule builder
->save(); // Automatically dispatches `Zap\Events\ScheduleCreated`
// Listen to events
event(new Zap\Events\ScheduleCreated($schedule));
API Endpoints: Expose availability checks and booking logic via API:
Route::get('/bookable-slots/{resource}', function ($resource) {
return response()->json($resource->getBookableSlots(now()->format('Y-m-d'), 30, 10));
});
Frontend Integration:
Use the getBookableSlots method to power frontend calendars (e.g., FullCalendar):
// Fetch slots for a date
fetch(`/api/bookable-slots/${resourceId}?date=${date}`)
.then(response => response.json())
.then(slots => {
// Render slots in calendar
});
Validation: Use Zap’s validation rules in Laravel’s form requests:
use Zap\Rules\NoOverlap;
public function rules()
{
return [
'start_time' => ['required', new NoOverlap($resource, '2025-01-15', '09:00', '10:00')],
];
}
Testing: Use Zap’s testing helpers to assert schedule behavior:
public function test_availability()
{
$doctor = Doctor::factory()->create();
$doctor->setAvailability('09:00', '17:00', ['monday', 'tuesday']);
$this->assertTrue($doctor->isBookableAt('2025-01-06', 60)); // Monday
$this->assertFalse($doctor->isBookableAt('2025-01-07', 60)); // Tuesday (if not in availability)
}
Timezone Mismatches:
config/app.php:
'timezone' => 'UTC',
Zap::for($resource)
->named('Timezone-Specific Schedule')
->availability()
->timezone('America/New_York')
->addPeriod('09:00', '17:00')
->weekly(['monday', 'friday'])
->save();
UUID/ULID Primary Keys:
// app/Models/Schedule.php
use Zap\Models\Schedule as BaseSchedule;
use Illuminate\Database\Eloquent\Concerns\HasUuids;
class Schedule extends BaseSchedule
{
use HasUuids;
}
How can I help you explore Laravel packages today?