Calendar (yoyaku_calendar)¶
yoyaku_calendar is the public, end-user face: a calendar Form API element
that shows a resource's slots, lets a visitor pick a day and a quantity per
category, and - on submit - places the holds. That is all it does;
everything after (payment, validation) belongs to other modules.
flowchart LR
FEED[Availability feed<br/>/booking/availability/{resource}] --> JS[Calendar JS<br/>month grid, coloured days]
JS -->|pick day → categories + qty| SEL[selection: categories:{id:qty}]
SEL -->|submit| BK[SelectionBooker]
BK -->|hold() per category| ENG[BookingManager]
The element¶
$form['calendar'] = ['#type' => 'yoyaku_calendar', '#resource' => $resource_id];
It renders a month grid coloured by availability (green = available, orange =
partial, red = full), navigable month to month. Clicking a day reveals that
day's slots; each slot lists its categories with the current availability
("12 left") and a quantity input. The element's value is
['selection' => '{"categories":{"<id>":<qty>}}'].
Embed it in any form - a node page, a Webform (via a future yoyaku_webform),
or use the shipped block (Yoyaku booking calendar, pick a resource) which
wraps it and places the holds on submit.
Selection modes¶
A resource's calendar_selection sets how many days a visitor may pick:
- single (the default): exactly one day.
- multi: several separate days, not necessarily consecutive.
- range: a contiguous span of days (no gaps).
selection_min_days and selection_max_days bound the day count for multi
and range; single is always capped at one.
range enforces that the picked days are consecutive, but it does not
impose a minimum of its own: a range resource with no selection_min_days
accepts a single day. That is by design, so a contiguous-only resource can
still be booked for one day (a meeting room hired for the day, say). For an
overnight stay, set selection_min_days (for example 2) so a one-day "range"
is rejected; the Holiday Cottage in the example module does this, while the
Meeting Room leaves the minimum unset on purpose.
Availability feed¶
GET /booking/availability/{yoyaku_resource}?month=YYYY-MM returns per-day
status + per-slot/category remaining (computed via
BookingManager::availability()). It is cacheable on purpose - short
max-age + the slot/category list tags (not the volatile booking list) - so a
month opening to a thundering herd doesn't recompute per request. Display is
best-effort; the hold() re-checks capacity authoritatively, and the engine's
fail-fast pre-check spares the lock once a slot is full.
What it does not do¶
No payment, confirmation, validation or orders - it produces held bookings
and stops. Permissions: view booking availability (the feed) and
place booking (the holds), both grantable to anonymous for public booking.