Laravel Zap

repository·main·Indexed 23 days ago

https://github.com/ludoguenet/laravel-zap

A Laravel package for managing complex scheduling patterns, recurrence, and availability checks. It provides tools to define availability, blocked time, and appointments for resources, with support for diverse recurrence frequencies (daily, weekly, bi-weekly, monthly, and ordinal weekdays). Key features include calculating bookable slots with configurable buffers, checking resource availability via isBookableAt and isBookableAtTime, and querying schedules using specialized model scopes. Requires PHP ≥8.5 and Laravel ≥13.0.

Tokens
12.4K
Snippets
29
Records
66
Agent score
81%

What's inside laravel-zap

  1. Understand Zap schedule types

    main

    Zap uses four distinct schedule types, each with different overlap behaviors:

    TypePurposeOverlap behavior
    AvailabilityDefine when resources can be bookedAllows overlaps
    AppointmentActual bookings or scheduled eventsExclusive — no overlaps allowed
    BlockedPeriods where booking is forbiddenExclusive — no overlaps allowed
    CustomNeutral schedules with explicit rulesYou define the rules
  2. Install Zap for Laravel

    main

    To install Zap, require the package via Composer, publish the service provider, and run your migrations.

    Requirements:

    • PHP ≥8.5
    • Laravel ≥13.0

    Note on UUIDs/ULIDs: If your application uses non-integer primary keys, you must configure custom model support (see Custom model support (UUIDs)) before running migrations.

    composer require laraveljutsu/zap
    php artisan vendor:publish --provider="Zap\ZapServiceProvider"
    
    php artisan migrate
  3. Create schedules using the fluent builder

    main

    Use the Zap facade or the zap() helper to build schedules. You must specify a model instance using for($model), define the type (e.g., availability(), blocked(), appointment(), or custom()), set the date/time range, and call save() to persist.

    use Zap\Facades\Zap;
    
    // Define availability (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();
    
    // Block time (lunch break)
    Zap::for($doctor)
        ->named('Lunch Break')
        ->blocked()
        ->forYear(2025)
        ->addPeriod('12:00', '13:00')
        ->weekly(['monday', 'tuesday', 'wednesday', 'thursday', 'friday'])
        ->save();
    
    // Create 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();
    
    // Custom schedule with explicit overlap rules
    Zap::for($user)
        ->named('Custom Event')
        ->custom()
        ->from('2025-01-15')
        ->addPeriod('15:00', '16:00')
        ->noOverlap()
        ->save();
  4. Configure Zap settings

    main

    Global scheduling behavior is managed in config/zap.php. Key configuration sections include:

    • default_rules: Defines which schedule types (e.g., appointment, blocked) have no_overlap enabled by default.
    • conflict_detection: Controls whether conflict detection is enabled and sets buffer_minutes.
    • time_slots: Configures buffer_minutes for time slots.
    • validation: Sets constraints like require_future_dates, max_date_range, min_period_duration, and max_periods_per_schedule.
    'default_rules' => [
        'no_overlap' => [
            'enabled' => true,
            'applies_to' => ['appointment', 'blocked'],
        ],
    ],
    
    'conflict_detection' => [
        'enabled' => true,
        'buffer_minutes' => 0,
    ],
    
    'time_slots' => [
        'buffer_minutes' => 0,
    ],
    
    'validation' => [
        'require_future_dates' => true,
        'max_date_range' => 365,
        'min_period_duration' => 15,
        'max_periods_per_schedule' => 50,
    ],
  5. Configure buffer time for slots

    main

    Buffer time adds required spacing between slots. You can configure a global default or specify it per-call.

    Global Configuration: In config/zap.php:

    'time_slots' => [
        'buffer_minutes' => 15, // Default buffer between slots
    ],

    Per-call Configuration: Pass the buffer in minutes as the third argument to getBookableSlots.

    Example behavior: With a 15-minute buffer and 60-minute slots:

    • Slot 1: 09:00 - 10:00
    • Slot 2: 10:15 - 11:15 (15 min buffer after slot 1)
    • Slot 3: 11:30 - 12:30
    $slots = $doctor->getBookableSlots('2025-01-15', 60, 15); // 15 min buffer
  6. Access Schedule metadata and configuration

    main

    The Schedule model supports several fields for storing extended configuration and metadata:

    • metadata (array): A generic array for storing additional unstructured data.
    • frequency (Frequency enum): Defines how the schedule repeats.
    • frequency_config (FrequencyConfig object): Stores specific configuration for the recurrence pattern (e.g., specific days of the week).

    These fields are automatically cast to their respective types (array, Frequency, and FrequencyConfig) when accessing them via the model.

  7. Install and publish Zap configuration and migrations

    main

    After installing the package via Composer, you need to publish the configuration file and database migrations to your Laravel application to customize settings and set up the required database schema.

    Use the following Artisan commands to publish these assets:

    1. Publish Configuration: Copies config/zap.php to your application's config/ directory.
    2. Publish Migrations: Copies the package migrations to your database/migrations/ directory.

    Note: You must run the migrations after publishing them using php artisan migrate.

  8. Configure Monthly Ordinal Weekday Recurrence

    main

    You can recur on the 1st, 2nd, 3rd, 4th, or last occurrence of a specific weekday each month using dynamic method names.

    Replace {Day} with Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, or Saturday in the following patterns:

    • first{Day}OfMonth()
    • second{Day}OfMonth()
    • third{Day}OfMonth()
    • fourth{Day}OfMonth()
    • last{Day}OfMonth()
    // Every 1st Wednesday of the month
    Zap::for($resource)
        ->named('Monthly Standup')
        ->appointment()
        ->firstWednesdayOfMonth()
        ->forYear(2025)
        ->addPeriod('09:00', '10:00')
        ->save();
    
    // Every last Monday of the month
    Zap::for($resource)
        ->named('Month-End Retro')
        ->appointment()
        ->lastMondayOfMonth()
        ->addPeriod('16:00', '17:00')
        ->save();
  9. Example: Implement a Doctor Appointment System

    main

    This workflow demonstrates setting up office hours, blocking time, checking availability, and booking an appointment.

    1. Set up availability: Use Zap::for($resource)->availability() to define recurring weekly hours.
    2. Block time: Use Zap::for($resource)->blocked() to create exceptions like lunch breaks.
    3. Check availability: Use isBookableAtTime before attempting a booking.
    4. Book appointment: Use Zap::for($resource)->appointment() to create the actual schedule entry.
    use Zap//Facades/Zap;
    
    // 1. Set up doctor's 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();
    
    // 2. Block lunch time
    Zap::for($doctor)
        ->named('Lunch Break')
        ->blocked()
        ->forYear(2025)
        ->weekly(['monday', 'tuesday', 'wednesday', 'thursday', 'friday'])
        ->addPeriod('12:00', '13:00')
        ->save();
    
    // 3. Get available slots for booking
    $slots = $doctor->getBookableSlots('2025-01-15', 60, 15);
    
    // 4. Check if specific time is available before booking
    if ($doctor->isBookableAtTime('2025-01-15', '10:00', '11:00')) {
        // Book the appointment
        Zap::for($doctor)
            ->named('Patient Consultation')
            ->appointment()
            ->from('2025-01-15')
            ->addPeriod('10:00', '11:00')
            ->withMetadata(['patient_id' => $patientId])
            ->save();
    }
    
    // 5. Find next available slot for a patient
    $nextSlot = $doctor->getNextBookableSlot(now()->format('Y-m-d'), 60);