LaravelPackages.net
Acme Inc.
Toggle sidebar
laraveljutsu/zap

A flexible, performant, and developer-friendly schedule management system for Laravel

100.673
1.447
v1.15.4
About laraveljutsu/zap

laraveljutsu/zap is a Laravel package for a flexible, performant, and developer-friendly schedule management system for laravel. It currently has 1.447 GitHub stars and 100.673 downloads on Packagist (latest version v1.15.4). Install it with composer require laraveljutsu/zap. Discover more Laravel packages by laraveljutsu or browse all Laravel packages to compare alternatives.

Last updated

Zap Logo

Flexible schedule management for modern Laravel applications

PHP Version Laravel Version License Total Downloads Why PHP

Website β€’ Documentation β€’ Support


Table of contents


🎯 What is Zap?

Zap is a calendar and scheduling package for Laravel. Define availabilities, appointments, blocked times, and custom schedules for any resource (doctors, rooms, employees, etc.).

Use cases: appointment booking, healthcare resources, employee shifts, shared space booking.


πŸ“¦ Installation

Requirements: PHP β‰₯8.5 β€’ Laravel β‰₯13.0

composer require laraveljutsu/zap
php artisan vendor:publish --provider="Zap\ZapServiceProvider"

UUIDs/ULIDs: If your app uses non-integer primary keys, read Custom model support before migrating. You may need to change migrations and config.

php artisan migrate

Make a model schedulable: add the HasSchedules trait.

use Zap\Models\Concerns\HasSchedules;

class Doctor extends Model
{
    use HasSchedules;
}

🧩 Core concepts

| Type | Purpose | Overlaps | |-----------------|----------------------------------|-----------------| | Availability | When a resource can be booked | Allowed | | Appointment | Bookings / scheduled events | Exclusive | | Blocked | When booking is forbidden | Exclusive | | Custom | Your rules (overlap, etc.) | You define |


πŸš€ Quick start

use Zap\Facades\Zap;

// 1. 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();

// 2. Block lunch
Zap::for($doctor)
    ->named('Lunch Break')
    ->blocked()
    ->forYear(2025)
    ->addPeriod('12:00', '13:00')
    ->weekly(['monday', 'tuesday', 'wednesday', 'thursday', 'friday'])
    ->save();

// 3. Create an 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();

// 4. Get bookable slots (60 min, 15 min buffer)
$slots = $doctor->getBookableSlots('2025-01-15', 60, 15);

// 5. Next available slot
$nextSlot = $doctor->getNextBookableSlot('2025-01-15', 60, 15);

πŸ’‘ Use the zap() helper instead of the facade when you prefer: zap()->for($doctor)->...


πŸ“… Schedule patterns

Recurrence at a glance

| Pattern | Method / example | |----------------------|------------------| | Daily | daily() | | Weekly (days) | weekly(['monday', 'friday']) | | Weekly + period | weekDays(['monday', 'friday'], '09:00', '17:00') | | Odd/even weeks | weeklyOdd(), weeklyEven() (+ weekOddDays / weekEvenDays) | | Bi-weekly | biweekly(['tuesday'], $startsOn?) | | Monthly (dates) | monthly(['days_of_month' => [1, 15]]) | | Bi-monthly / quarter / semi / annual | bimonthly(), quarterly(), semiannually(), annually() + config | | Ordinal weekday | firstWednesdayOfMonth(), secondFridayOfMonth(), lastMondayOfMonth() | | Every N weeks | everyThreeWeeks(), … everyFiftyTwoWeeks() | | Every N months | everyFourMonths(), … everyElevenMonths() |

Recurrence examples

Daily & weekly

$schedule->daily()->from('2025-01-01')->to('2025-12-31');
$schedule->weekly(['monday', 'wednesday', 'friday'])->forYear(2025);
$schedule->weekDays(['monday', 'wednesday', 'friday'], '09:00', '17:00')->forYear(2025);
$schedule->weeklyOdd(['monday', 'wednesday', 'friday'])->forYear(2025);
$schedule->weeklyEven(['monday', 'wednesday', 'friday'])->forYear(2025);
$schedule->biweekly(['tuesday', 'thursday'], '2025-01-07')->from('2025-01-07')->to('2025-03-31');

Monthly (by day of month)

$schedule->monthly(['days_of_month' => [1, 15]])->forYear(2025);
$schedule->bimonthly(['days_of_month' => [5, 20], 'start_month' => 2])->from('2025-01-05')->to('2025-06-30');
$schedule->quarterly(['days_of_month' => [7, 21], 'start_month' => 2])->from('2025-02-15')->to('2025-11-15');
$schedule->semiannually(['days_of_month' => [10], 'start_month' => 3])->from('2025-03-10')->to('2025-12-10');
$schedule->annually(['days_of_month' => [1, 15], 'start_month' => 4])->from('2025-04-01')->to('2026-04-01');

Monthly ordinal weekday (1st, 2nd, 3rd, 4th, or last weekday of the month)

$schedule->firstWednesdayOfMonth()->forYear(2025);   // Every 1st Wednesday
$schedule->secondFridayOfMonth()->forYear(2025);     // Every 2nd Friday
$schedule->lastMondayOfMonth()->forYear(2025);      // Every last Monday
// Also: thirdTuesdayOfMonth(), fourthSaturdayOfMonth(), lastSundayOfMonth(), etc.

Dynamic intervals

$schedule->everyThreeWeeks(['monday', 'friday'])->from('2025-01-06')->to('2025-12-31');
$schedule->everyFourWeeks(['tuesday'], '2025-01-06')->from('2025-01-13');
$schedule->everyFourMonths(['day_of_month' => 15])->forYear(2025);
$schedule->everyFiveMonths(['days_of_month' => [1, 15], 'start_month' => 2])->forYear(2025);

Date ranges

$schedule->from('2025-01-15');                           // Start
$schedule->on('2025-01-15');                             // Alias for from()
$schedule->from('2025-01-01')->to('2025-12-31');        // Range
$schedule->between('2025-01-01', '2025-12-31');          // Same
$schedule->forYear(2025);                                // Full year

Time periods

$schedule->addPeriod('09:00', '17:00');
$schedule->addPeriod('09:00', '12:00');
$schedule->addPeriod('14:00', '17:00');

πŸ” Query & check availability

| Need | Method | |--------------------------|--------| | Any bookable slot today? | $model->isBookableAt('2025-01-15', 60) | | Time range bookable? | $model->isBookableAtTime('2025-01-15', '09:00', '09:30') | | Time range bookable (custom slot)? | $model->isBookableAtTime('2025-01-15', '09:30', '10:00', null, 30) | | Time range bookable (custom slot + buffer)? | $model->isBookableAtTime('2025-01-15', '09:45', '10:15', null, 30, 15) | | List bookable slots | $model->getBookableSlots('2025-01-15', 60, 15) | | Next bookable slot | $model->getNextBookableSlot('2025-01-15', 60, 15) | | Conflicts for a schedule | Zap::findConflicts($schedule) / Zap::hasConflicts($schedule) | | Schedules on a date | $model->schedulesForDate('2025-01-15')->get() | | Schedules in range | $model->schedulesForDateRange('2025-01-01', '2025-01-31')->get() | | By type | $model->appointmentSchedules(), availabilitySchedules(), blockedSchedules() | | Schedule type checks | $schedule->isAvailability(), isAppointment(), isBlocked() |

⚠️ isAvailableAt() is deprecated. Prefer isBookableAt(), isBookableAtTime(), and getBookableSlots().


πŸ’Ό Real-world examples

Doctor

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();

Zap::for($doctor)->named('Lunch Break')->blocked()->forYear(2025)
    ->addPeriod('12:00', '13:00')
    ->weekly(['monday', 'tuesday', 'wednesday', 'thursday', 'friday'])->save();

Zap::for($doctor)->named('Patient A - Checkup')->appointment()
    ->from('2025-01-15')->addPeriod('10:00', '11:00')->withMetadata(['patient_id' => 1])->save();

$slots = $doctor->getBookableSlots('2025-01-15', 60, 15);

Meeting room

Zap::for($room)->named('Conference Room A')->availability()
    ->weekDays(['monday', 'tuesday', 'wednesday', 'thursday', 'friday'], '08:00', '18:00')
    ->forYear(2025)->save();

Zap::for($room)->named('Board Meeting')->appointment()
    ->from('2025-03-15')->addPeriod('09:00', '11:00')
    ->withMetadata(['organizer' => '[email protected]'])->save();

Employee (with vacation)

Zap::for($employee)->named('Regular Shift')->availability()
    ->weekDays(['monday', 'tuesday', 'wednesday', 'thursday', 'friday'], '09:00', '17:00')
    ->forYear(2025)->save();

Zap::for($employee)->named('Vacation Leave')->blocked()
    ->between('2025-06-01', '2025-06-15')->addPeriod('00:00', '23:59')->save();

βš™οΈ Configuration

Publish assets:

php artisan vendor:publish --tag=zap-migrations
php artisan vendor:publish --tag=zap-config

Important keys in config/zap.php: time_slots.buffer_minutes, default_rules.no_overlap, conflict_detection, validation.


πŸ›‘οΈ Advanced features

Custom schedules & rules

Zap::for($user)->named('Custom Event')->custom()
    ->from('2025-01-15')->addPeriod('15:00', '16:00')->noOverlap()->save();

Metadata

->withMetadata(['patient_id' => 1, 'type' => 'consultation', 'notes' => 'Follow-up'])

Validation rules: noOverlap(), allowOverlap(), workingHoursOnly('09:00', '17:00'), maxDuration(120), noWeekends().

Custom model support (UUIDs)

If your app uses UUIDs/ULIDs for primary keys:

  1. Models β€” Extend Zap\Models\Schedule and Zap\Models\SchedulePeriod, add Laravel’s HasUuids trait. Add HasUuids to your schedulable model (e.g. Doctor) as well.
  2. Config β€” In config/zap.php, set models.schedule and models.schedule_period to your extended classes.
  3. Migrations β€” After publishing, change id() to uuid('id')->primary(), morphs('schedulable') to uuidMorphs('schedulable'), and foreignId('schedule_id') to foreignUuid('schedule_id') in the schedules and schedule_periods tables.

Do this before running migrations.


🧠 AI agent support

Zap provides Laravel Boost 2.0 skills. With Boost installed, agents get accurate knowledge of the API.

| Skill | Contents | |-------------------|----------| | zap-schedules | Types, builder API, validation, conflict detection | | zap-availability | Bookable slots, availability checks, querying | | zap-recurrence | All recurrence patterns (daily, weekly, odd/even, monthly, ordinal weekday, dynamic) |

No extra configuration.


🀝 Contributing

Contributions are welcome. Use PSR-12 and add tests.

git clone https://github.com/ludoguenet/laravel-zap.git
cd laravel-zap
composer install
composer pest

πŸ“„ License

MIT License.

πŸ”’ Security

Report issues to [email protected] (not the public issue tracker).


Made with πŸ’› by Ludovic GuΓ©net for the Laravel community

Star History Chart