API Reference

Bookable Slots

Using availability-based scheduling with getBookableSlots and getNextBookableSlot methods.

Overview

The bookable slots feature provides availability-based scheduling that automatically respects your availability schedules and conflicts. Unlike getAvailableSlots() which generates slots within a time range, getBookableSlots() only generates slots that intersect with your defined availability periods.

Key Differences

MethodPurposeTime RangeAvailability Respect
getAvailableSlots()Generate slots in time rangeFixed start/end timesChecks conflicts only
getBookableSlots()Generate slots from availabilityBased on availability periodsRespects availability schedules

Basic Usage

Getting Bookable Slots

// First, create availability schedules
$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();

// Get bookable slots for a specific date
$slots = $doctor->getBookableSlots(
    date: '2025-01-15', // Wednesday
    slotDuration: 60,
    bufferMinutes: 15
);

foreach ($slots as $slot) {
    if ($slot['is_available']) {
        echo "Bookable: {$slot['start_time']} - {$slot['end_time']}";
    }
}

Finding Next Bookable Slot

// Find the next available bookable slot
$nextSlot = $doctor->getNextBookableSlot(
    afterDate: '2025-01-15',
    duration: 90, // 1.5 hours needed
    bufferMinutes: 10
);

if ($nextSlot) {
    echo "Next bookable: {$nextSlot['date']} from {$nextSlot['start_time']} to {$nextSlot['end_time']}";
}

Advanced Examples

Multiple Availability Periods

// Create availability with multiple periods
$availability = Zap::for($stylist)
    ->named('Working Hours')
    ->availability()
    ->from('2025-01-01')->to('2025-12-31')
    ->addPeriod('09:00', '12:00') // Morning
    ->addPeriod('14:00', '18:00') // Afternoon
    ->weekly(['monday', 'tuesday', 'wednesday', 'thursday', 'friday'])
    ->save();

// Get bookable slots - will only show slots within these periods
$slots = $stylist->getBookableSlots('2025-01-15', 45, 15);

// Results: 9:00-9:45, 10:00-10:45, 11:00-11:45, 2:00-2:45, 3:00-3:45, etc.
// No slots during lunch break (12:00-14:00)

Handling Conflicts

// Create availability
$availability = Zap::for($doctor)
    ->named('Office Hours')
    ->availability()
    ->from('2025-01-15')
    ->addPeriod('09:00', '17:00')
    ->save();

// Create existing appointment
$appointment = Zap::for($doctor)
    ->named('Patient A - Checkup')
    ->appointment()
    ->from('2025-01-15')
    ->addPeriod('10:00', '11:00')
    ->save();

// Get bookable slots - 10:00-11:00 will be marked as unavailable
$slots = $doctor->getBookableSlots('2025-01-15', 60, 15);

foreach ($slots as $slot) {
    if ($slot['is_available']) {
        echo "Available: {$slot['start_time']} - {$slot['end_time']}";
    } else {
        echo "Blocked: {$slot['start_time']} - {$slot['end_time']}";
    }
}

Recurring Availability

// Create weekly recurring availability
$availability = Zap::for($trainer)
    ->named('Gym Sessions')
    ->availability()
    ->from('2025-01-01')
    ->addPeriod('06:00', '09:00') // Morning sessions
    ->addPeriod('18:00', '21:00') // Evening sessions
    ->weekly(['monday', 'wednesday', 'friday'])
    ->save();

// Get slots for different days
$mondaySlots = $trainer->getBookableSlots('2025-01-06', 60); // Monday - has slots
$tuesdaySlots = $trainer->getBookableSlots('2025-01-07', 60); // Tuesday - no slots
$wednesdaySlots = $trainer->getBookableSlots('2025-01-08', 60); // Wednesday - has slots

echo count($mondaySlots); // > 0
echo count($tuesdaySlots); // 0
echo count($wednesdaySlots); // > 0

Real-World Examples

Healthcare System

// Doctor's 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
$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();

// Get bookable consultation slots
$consultationSlots = $doctor->getBookableSlots('2025-01-15', 30, 10);

// Only shows slots within office hours, excludes lunch break
foreach ($consultationSlots as $slot) {
    if ($slot['is_available']) {
        echo "Available consultation: {$slot['start_time']} - {$slot['end_time']}";
    }
}

Service Business

// Hair stylist availability
$availability = Zap::for($stylist)
    ->named('Working Hours')
    ->availability()
    ->from('2025-01-01')->to('2025-12-31')
    ->addPeriod('09:00', '18:00')
    ->weekly(['tuesday', 'wednesday', 'thursday', 'friday', 'saturday'])
    ->save();

// Different service types with different durations and buffers
$haircutSlots = $stylist->getBookableSlots('2025-01-15', 45, 15); // 45min + 15min cleanup
$coloringSlots = $stylist->getBookableSlots('2025-01-15', 120, 30); // 2hr + 30min cleanup

// Find next available slot for any service
$nextSlot = $stylist->getNextBookableSlot(
    afterDate: '2025-01-15',
    duration: 60,
    bufferMinutes: 20
);

Meeting Room Booking

// Room availability
$availability = 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();

// Check if room is bookable for specific time
$slots = $room->getBookableSlots('2025-01-15', 60, 0);

$isBookable = collect($slots)->contains(function ($slot) {
    return $slot['start_time'] === '10:00' && $slot['is_available'];
});

if ($isBookable) {
    // Book the room
    $meeting = Zap::for($room)
        ->named('Team Meeting')
        ->appointment()
        ->from('2025-01-15')
        ->addPeriod('10:00', '11:00')
        ->withMetadata([
            'organizer' => 'john@company.com',
            'attendees' => 8
        ])
        ->save();
}

Best Practices

  1. Always create availability schedules first - Bookable slots require availability schedules to work
  2. Use appropriate buffer times - Match buffer time to your business needs (setup, cleanup, transition)
  3. Handle inactive schedules - Inactive availability schedules won't generate bookable slots
  4. Check slot availability - Always check is_available before booking
  5. Use metadata for context - Store additional information about slots and bookings
  6. Test edge cases - Verify behavior with overlapping schedules and complex availability patterns

Method Reference

getBookableSlots()

public function getBookableSlots(
    string $date,
    int $slotDuration = 60,
    ?int $bufferMinutes = null
): array

Parameters:

  • $date - Date to get slots for (Y-m-d format)
  • $slotDuration - Duration of each slot in minutes
  • $bufferMinutes - Buffer time between slots (null = use config)

Returns: Array of slot data with start_time, end_time, is_available, and buffer_minutes

getNextBookableSlot()

public function getNextBookableSlot(
    ?string $afterDate = null,
    int $duration = 60,
    ?int $bufferMinutes = null
): ?array

Parameters:

  • $afterDate - Start searching from this date (null = today)
  • $duration - Duration needed in minutes
  • $bufferMinutes - Buffer time between slots (null = use config)

Returns: Next available slot data or null if none found within 30 days