Skip to content

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.