Dostupnosť a sloty
Dostupnosť nevie nič o rezerváciách — pýta sa zdroja obsadenosti, čo je obsadené. availability() vráti nemenný builder; každý setter vráti kópiu:
use Carbon\CarbonImmutable;
use RoundlyConsulting\OpeningHours\Availability\BusyPeriod;
use RoundlyConsulting\OpeningHours\Facades\OpeningHours;
$availability = OpeningHours::for($clinic)->availability()
->withBusyPeriods(fn (CarbonImmutable $start, CarbonImmutable $end) => $bookings->between($start, $end))
->capacity(2) // parallel bookings (a range's own capacity wins)
->buffers(before: 5, after: 10) // must fall inside opening hours by default
->minNotice(minutes: 120)
->horizon(days: 60);
$result = $availability->check($start, $end); // AvailabilityResult
$result->available; // bool
$result->reason; // ?UnavailableReason: invalid_range, in_past, too_soon, too_far, closed, busy
$result->conflict; // ?BusyPeriod
$result->message(); // translated reason
$availability->isAvailable($start, $end, weight: 1);Nastavenia
| Metóda | Význam |
|---|---|
withBusyPeriods($busy) | Zdroj obsadenosti: BusyPeriodProvider, iterable s BusyPeriod alebo closure (start, end), ktorá ich vráti. |
capacity(int) | Predvolená paralelná kapacita (≥ 1); vlastná kapacita rozsahu má prednosť; inak 1. |
buffers(before:, after:, withinOpeningHours: true) | Minúty blokované okolo každej rezervácie; predvolene musia spadať do otváracích hodín. Pri withinOpeningHours: false sa zatvorená časť rezervy počíta s kapacitou rozsahu, v ktorom rezervácia začína (before) alebo končí (after). |
minNotice(minutes:) | Najskorší začiatok = teraz + n skutočných minút. |
horizon(days:) | Najneskorší začiatok = teraz + n kalendárnych dní podľa miestneho času (vrátane); null = bez limitu. |
at($instant) | Vyhodnotiť k tomuto okamihu namiesto teraz. |
Prečo termín nie je dostupný
check() vyhodnocuje v pevnom poradí a vráti prvý dôvod zlyhania; message() ho vráti preložený:
| Dôvod | Kedy |
|---|---|
invalid_range | Koniec nie je po začiatku. |
in_past | Začiatok je v minulosti. |
too_soon | Začiatok spadá do minimálneho predstihu. |
too_far | Začiatok je za horizontom. |
closed | Otváracie hodiny nepokrývajú rezerváciu aj s časovými rezervami. |
busy | Kapacita je niekde v obsadenom okne prekročená; conflict obsahuje prvé prekrývajúce sa obsadené obdobie. |
Sloty
$slots = $availability->slots('2026-10-01', '2026-10-07') // whole local days, inclusive
->duration(30)->step(15)->alignTo(15)
->limit(200)
->get(); // SlotCollection
$slots->groupByDate(); // ['2026-10-01' => [Slot, …], …]
foreach ($slots as $slot) {
$slot->start; // CarbonImmutable
$slot->end;
$slot->remainingCapacity;
}
$all = $availability->slots('2026-10-01', '2026-10-01')
->duration(60)
->includeUnavailable() // also slots that failed on capacity
->get();
$all->available(); // only the bookable ones
$availability->slots('2026-10-01', '2026-10-01')->duration(60)->first(); // ?Slot
$availability->nextAvailableSlot(duration: 30); // ?Slot
$availability->freePeriods('2026-10-01', '2026-10-01'); // list<Period>- slots($from, $to) prijíma dátumy (celé miestne dni vrátane) alebo okamihy (koniec vylúčený). duration() je povinné; step() je predvolene rovné dĺžke; alignTo() je predvolene rovné kroku a počíta sa od miestnej polnoci.
- weight() rezervuje naraz viac jednotiek kapacity; includeUnavailable() vráti aj sloty, ktoré zlyhali na kapacite (dôvod busy); limit() obmedzí výsledok na limits.slots.
- get() vráti SlotCollection s metódami available(), groupByDate() a toArray(); first() vráti prvý ?Slot.
- Každý Slot má start, end, available, remainingCapacity a reason.
- nextAvailableSlot() prezerá search_days miestnych dní dopredu a hľadá po častiach, takže sa sám zmestí do max_query_days.
- Mriežka sa zarovnáva podľa miestneho času a ostáva správna aj pri preskočenej hodine a 30-minútových posunoch; viacdňové sloty (prenájmy) fungujú rovnako.
Model kapacity
Kapacita otvorenia je range.capacity ?? kapacita dostupnosti ?? 1, pričom prekrývajúce sa rozsahy berú maximum. Rezervácia sa zmestí, ak usage + weight ≤ capacity v každom okamihu [start − before, end + after). Pri buffers(withinOpeningHours: false) sa zatvorená časť rezervy počíta s kapacitou rozsahu, v ktorom rezervácia začína (before) alebo končí (after). Dĺžka, predstih aj časové rezervy sú skutočný uplynulý čas; horizont sa počíta v kalendárnych dňoch podľa miestneho času.
Pri rezervácii overte znova
Dostupnosť je iba čítanie. Keď rezerváciu ukladáte, overte ju znova vo vlastnom zámku alebo cez unikátne obmedzenie — medzi kontrolou a zápisom môže ten istý slot rezervovať iný request:
use Illuminate\Support\Facades\Cache;
Cache::lock("book:{$vet->id}:{$start}", 10)->block(5, function () use ($availability, $vet, $start, $end): void {
abort_unless($availability->isAvailable($start, $end), 409);
Appointment::query()->create(['vet_id' => $vet->id, 'starts_at' => $start, 'ends_at' => $end]);
});Prejavte lásku k open source
Tento balík je zadarmo pod licenciou MIT. Ak vám šetrí čas, jednorazový príspevok alebo členstvo na Patreone nám pomôže ho ďalej udržiavať, testovať a dokumentovať.
Ďalšie spôsoby podpory vrátane kryptomienOdoslaním daru súhlasíte s našimi podmienkami prijímania darov.
Chcete to zabudovať do svojho produktu?
Naše balíky integrujeme do zákazkových Laravel a AI riešení. Napíšte nám, na čom pracujete, a ozveme sa do 48 hodín.