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:
use Zap\Models\Concerns\HasSchedules;
class Doctor extends Model
{
use HasSchedules;
}
First use case: Define working hours for a doctor:
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();
Zap::for($model)->named()->[availability|appointment|blocked]()->...->save()/schedule-patterns section for common use cases/query--check-availability for availability checksDefine Schedules:
Zap::for($resource)
->named('Schedule Name')
->[availability|appointment|blocked]()
->[recurrencePattern]()
->[timePeriods]()
->save();
Query Availability:
$slots = $resource->getBookableSlots('2025-01-15', 60, 15);
$nextSlot = $resource->getNextBookableSlot('2025-01-15', 60, 15);
Create Appointments:
Zap::for($resource)
->named('Appointment Name')
->appointment()
->from('2025-01-15')
->addPeriod('10:00', '11:00')
->withMetadata(['patient_id' => 1])
->save();
Validation: Use noOverlap(), maxDuration(), or workingHoursOnly() for custom rules:
->noOverlap()
->maxDuration(120)
->workingHoursOnly('09:00', '17:00')
Recurrence Patterns: Combine patterns for complex schedules:
->weeklyOdd(['monday', 'wednesday'])
->everyThreeWeeks(['friday'])
Metadata: Attach custom data to schedules:
->withMetadata(['room_type' => 'conference', 'capacity' => 10])
Conflict Detection:
if (Zap::hasConflicts($schedule)) {
// Handle conflict
}
| Use Case | Implementation Pattern |
|---|---|
| Doctor Appointments | appointment() + getBookableSlots() |
| Room Bookings | availability() + blocked() |
| Employee Shifts | weekDays() + biweekly() |
| Recurring Events | firstWednesdayOfMonth() + addPeriod() |
| Custom Validation | noOverlap() + maxDuration() |
Timezone Handling:
America/New_York but Zap expects UTC, week boundaries (e.g., startOfWeek) may misalign.UUID/ULID Models:
migrate:
php artisan vendor:publish --tag=zap-migrations
php artisan vendor:publish --tag=zap-config
uuid() and uuidMorphs() instead of foreignId().Deprecated Methods:
isAvailableAt(). Use isBookableAt() or isBookableAtTime() instead.Overlap Logic:
noOverlap() prevents appointments from conflicting with availabilities/blocked times. Explicitly allow overlaps with allowOverlap() if needed.Date Ranges:
forYear(2025) creates schedules for every occurrence in the year. For large ranges, consider from()->to() with explicit recurrence patterns.Check Conflicts:
$conflicts = Zap::findConflicts($schedule);
dd($conflicts);
Inspect Schedules:
$doctor->schedules()->get(); // All schedules
$doctor->appointmentSchedules()->get(); // Only appointments
Log Recurrence:
Enable debug mode in config/zap.php:
'debug' => env('ZAP_DEBUG', false),
Time Period Validation:
Ensure addPeriod() uses 24-hour format (e.g., '09:00' not '9:00 AM').
Custom Schedule Types:
Extend Zap\Models\Schedule to add domain-specific logic:
namespace App\Models;
use Zap\Models\Schedule as BaseSchedule;
class CustomSchedule extends BaseSchedule
{
protected $customRule = 'my_rule';
}
Validation Rules:
Add custom rules to Zap\Rules\ScheduleRule or override in your model:
use Zap\Rules\ScheduleRule;
class Doctor extends Model
{
use HasSchedules;
protected function customValidationRules()
{
return [
'max_patients_per_hour' => 5,
];
}
}
Query Scopes: Add global scopes to filter schedules:
namespace Zap\Models;
use Illuminate\Database\Eloquent\Builder;
class Schedule extends Model
{
public function scopeActive(Builder $query)
{
return $query->where('ends_at', '>', now());
}
}
Event Listeners:
Listen for schedule events (e.g., Zap\Events\ScheduleCreated):
use Zap\Events\ScheduleCreated;
Event::listen(ScheduleCreated::class, function ($schedule) {
// Send notification, log, etc.
});
Buffer Minutes:
Adjust time_slots.buffer_minutes in config/zap.php to control padding between slots (default: 15).
Conflict Detection:
Set conflict_detection to 'strict' or 'lenient' based on your overlap tolerance.
Default Rules:
Override default_rules to enforce global constraints:
'default_rules' => [
'no_overlap' => true,
'max_duration' => 120, // 2 hours
],
Indexing:
Ensure schedulable_id and schedule_type are indexed in the schedules table for large datasets.
Caching: Cache frequent availability queries:
$slots = Cache::remember("slots_{$doctor->id}_{$date}", now()->addHours(1), function () use ($doctor, $date) {
return $doctor->getBookableSlots($date, 60, 15);
});
Batch Processing:
Use Zap::for($model)->batch() for bulk schedule creation:
Zap::for($doctor)->batch()
->named('Bulk Appointments')
->appointment()
->from('2025-01-01')->to('2025-01-31')
->addPeriod('10:00', '11:00')
->save();
How can I help you explore Laravel packages today?