Skip to content

Conversation

SebastianWiz
Copy link
Contributor

Context

⛑️ Ticket(s): https://secure.helpscout.net/conversation/3084918923/89679?viewId=8172236

Summary

This PR adds a snippet that lets you set a daily booking limit for one or multiple services. The snippet hooks into Gravity Forms validation to block submissions once the combined quantity for that day reaches the limit and shows the “fully booked” validation message on the booking-time field. It also hooks gpb_before_booking_created so the same check runs during booking creation, preventing overbookings if multiple requests land at the same time.

Loom demo: https://www.loom.com/share/6cae41c053a647e48deec68868e23ef4

Copy link

coderabbitai bot commented Sep 26, 2025

Walkthrough

Adds class GPB_Daily_Service_Limit to enforce a shared per-day booking cap for specified GP Bookings services by validating booking creation via a pre-create guard and by filtering REST availability responses for tracked services.

Changes

Cohort / File(s) Summary
Daily booking limit feature
gp-bookings/gpb-daily-service-booking-limit.php
New class GPB_Daily_Service_Limit with constructor __construct(array $args). Public methods: guard_booking_creation(array $booking_data, $bookable) and filter_rest_availability($response, $server, $request). Adds helpers: is_tracked_service(int $service_id), normalize_booking_date($start, $end, $bookable): ?string, get_daily_totals(array $dates, $exclude_booking_id = null): array, get_totals_for_range(string $start_datetime, string $end_datetime, $exclude_booking_id = null): array, and exceeds_limit(array $dates, int $incoming_quantity = 0, $exclude_booking_id = null): bool. Hooks into gpb_before_booking_created and rest_post_dispatch. Includes example instantiation with service_ids and daily_limit.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    actor Client
    participant REST as REST Client
    participant API as WP REST /availability
    participant Filter as GPB_Daily_Service_Limit.filter_rest_availability
    participant DB as Booking Store
    note over Filter,DB #f0f8ff: When querying availability for tracked services
    REST->>API: Request availability for service/date range
    API->>Filter: rest_post_dispatch -> filter_rest_availability(response, server, request)
    Filter->>DB: get_daily_totals(dates) for tracked services
    DB-->>Filter: totals per date
    alt date total >= daily_limit
        Filter->>API: override response -> mark date unavailable
    else
        Filter->>API: leave response unchanged
    end
Loading
sequenceDiagram
    autonumber
    actor System
    participant Creator as Booking Creator
    participant Guard as GPB_Daily_Service_Limit.guard_booking_creation
    participant DB as Booking Store
    note over Guard,DB #f0fff0: On booking creation attempt
    Creator->>Guard: gpb_before_booking_created(booking_data, bookable)
    Guard->>Guard: normalize_booking_date(start,end,bookable)
    Guard->>DB: get_totals_for_range(start_datetime,end_datetime, exclude_booking_id?)
    DB-->>Guard: existing totals
    alt existing + incoming_quantity > daily_limit
        Guard-->>Creator: throw CapacityException (prevent creation)
    else
        Guard-->>Creator: allow creation to proceed
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Title Check ⚠️ Warning The current title includes the file name and a generic “Added new snippet” description, which does not clearly convey the snippet’s purpose of enforcing daily booking limits for one or more services and adds unnecessary noise for someone scanning the history. Rename the title to succinctly describe the new functionality, for example “Add daily service booking limit snippet” or “Enforce shared daily booking cap for GPB services.”
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed The description adheres to the repository template by providing a Context section with the ticket link and a Summary section that clearly explains the new snippet’s behavior and even includes a Loom demo, making it complete and informative.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch SebastianWiz-patch-3

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1ea2f3f and 8fb788f.

📒 Files selected for processing (1)
  • gp-bookings/gpb-daily-service-booking-limit.php (1 hunks)
🧰 Additional context used
🪛 GitHub Actions: PHP Lint (PR)
gp-bookings/gpb-daily-service-booking-limit.php

[error] 197-197: PHPCompatibility error: Nullable return types are not supported in PHP 7.0 or earlier. (source: PHPCompatibility.FunctionDeclarations.NewNullableTypes.returnTypeFound). Command/Step: Run thenabeel/action-phpcs@v8.

🪛 GitHub Check: PHPCS (Files Changed)
gp-bookings/gpb-daily-service-booking-limit.php

[failure] 197-197:
Nullable return types are not supported in PHP 7.0 or earlier.

🪛 PHPMD (2.15.0)
gp-bookings/gpb-daily-service-booking-limit.php

64-64: Avoid unused parameters such as '$server'. (undefined)

(UnusedFormalParameter)

🔇 Additional comments (6)
gp-bookings/gpb-daily-service-booking-limit.php (6)

24-40: LGTM!

The constructor properly validates configuration, normalizes service IDs to integers, and registers hooks only when the configuration is valid. The early return prevents unnecessary hook registration when the snippet is misconfigured.


42-62: LGTM!

The guard method correctly prevents booking creation when the daily limit would be exceeded. The validation chain (service type → date normalization → limit check) is sound, and throwing a CapacityException is the appropriate way to halt booking creation in GP Bookings.


64-110: LGTM!

The REST availability filter correctly marks days as unavailable when the daily limit is reached. The request validation is thorough, and the response modification properly sets the day status. The $server parameter flagged by PHPMD as unused is required by the rest_post_dispatch filter signature, so the warning is a false positive.


112-124: LGTM!

The limit-checking logic is correct: it properly aggregates existing totals per date and correctly uses > to determine if adding the incoming quantity would exceed the configured limit.


126-191: LGTM!

The booking query and aggregation logic is well-structured. The method correctly:

  • Constructs the datetime range from the provided dates
  • Queries bookings for tracked services with appropriate status filters
  • Defensively handles exceptions when retrieving service IDs
  • Normalizes booking dates before aggregating quantities
  • Sums quantities per day across all tracked services

209-215: LGTM!

The configuration example is clear and well-documented. The inline comments guide users to customize the service IDs and daily limit for their specific use case.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

github-actions bot commented Sep 26, 2025

Fails
🚫

Commit message 'gpb-daily-service-booking-limit.php: Added new snippet' needs to end in a period or exclamation.

🚫

Commit message 'gpb-daily-service-booking-limit.php: Added a new snippet' needs to end in a period or exclamation.

🚫

Commit message 'gpb-daily-service-booking-limit.php: Added new snippet' needs to end in a period or exclamation.

🚫

Commit message 'gpb-daily-service-booking-limit.php: Added new snippet' needs to end in a period or exclamation.

Warnings
⚠️ When ready, don't forget to request reviews on this pull request from your fellow wizards.

Generated by 🚫 dangerJS against 8fb788f

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8fd3967 and 5ef5330.

📒 Files selected for processing (1)
  • gp-bookings/gpb-daily-service-booking-limit.php (1 hunks)
🧰 Additional context used
🪛 GitHub Check: PHPCS (Files Changed)
gp-bookings/gpb-daily-service-booking-limit.php

[warning] 209-209:
Array double arrow not aligned correctly; expected 5 space(s) between "'form_id'" and double arrow, but found 1.


[warning] 151-151:
Equals sign not aligned with surrounding assignments; expected 3 spaces but found 6 spaces


[warning] 150-150:
Equals sign not aligned with surrounding assignments; expected 1 space but found 4 spaces


[failure] 33-33:
Tabs must be used to indent lines; spaces are not allowed


[failure] 32-32:
Tabs must be used to indent lines; spaces are not allowed


[failure] 31-31:
Tabs must be used to indent lines; spaces are not allowed


[failure] 30-30:
Tabs must be used to indent lines; spaces are not allowed


[failure] 29-29:
Tabs must be used to indent lines; spaces are not allowed


[failure] 28-28:
Tabs must be used to indent lines; spaces are not allowed


[failure] 26-26:
Tabs must be used to indent lines; spaces are not allowed


[failure] 25-25:
Tabs must be used to indent lines; spaces are not allowed


[failure] 24-24:
Tabs must be used to indent lines; spaces are not allowed


[failure] 23-23:
Tabs must be used to indent lines; spaces are not allowed

🪛 GitHub Actions: PHP Lint (PR)
gp-bookings/gpb-daily-service-booking-limit.php

[error] 23-23: Tabs must be used to indent lines; spaces are not allowed (Generic.WhiteSpace.DisallowSpaceIndent.SpacesUsed). PHPCS check failed.

🔇 Additional comments (2)
gp-bookings/gpb-daily-service-booking-limit.php (2)

54-69: Good concurrency guard before booking creation.

Validates against the cap with normalized dates and throws a CapacityException to prevent races. Solid safeguard.


112-116: Confirm expected behavior for multi‑day bookings.

Currently, the date normalized from start/end is reduced to the start day only. If multi‑day bookings should decrement capacity for each day in the range, this needs a loop over all dates in the span.

Do you intend to count only the start day, or every day in a multi‑day booking’s range?

@SebastianWiz SebastianWiz changed the title gpb-daily-service-booking-limit.php: Added new snippet gpb-daily-service-booking-limit.php: Added new snippet. Sep 26, 2025
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4a28114 and 1ea2f3f.

📒 Files selected for processing (1)
  • gp-bookings/gpb-daily-service-booking-limit.php (1 hunks)
🧰 Additional context used
🪛 GitHub Check: PHPCS (Files Changed)
gp-bookings/gpb-daily-service-booking-limit.php

[warning] 209-209:
Array double arrow not aligned correctly; expected 5 space(s) between "'form_id'" and double arrow, but found 1.


[failure] 193-193:
void return type is not present in PHP version 7.0 or earlier


[failure] 173-173:
Nullable return types are not supported in PHP 7.0 or earlier.


[failure] 82-82:
Short array syntax is not allowed

🪛 GitHub Actions: PHP Lint (PR)
gp-bookings/gpb-daily-service-booking-limit.php

[error] 82-82: PHPCS: Short array syntax is not allowed (Generic.Arrays.DisallowShortArraySyntax.Found)

🔇 Additional comments (2)
gp-bookings/gpb-daily-service-booking-limit.php (2)

97-105: Don't skip Select Service bookings when enforcing the cap

When the booking field uses “Select Service,” gpbService is empty, so $service_id stays 0 and we skip validation entirely, effectively disabling the limit for those forms. Please fall back to the posted child value before bailing out.

-			$service_id = isset( $service->gpbService ) ? (int) $service->gpbService : 0;
-			if ( ! $service_id || ! in_array( $service_id, $this->service_ids, true ) ) {
+			$service_id = isset( $service->gpbService ) ? (int) $service->gpbService : 0;
+			if ( ! $service_id ) {
+				$posted_service = $this->get_posted_value( (int) $service->id );
+				$service_id     = $posted_service ? (int) $posted_service : 0;
+			}
+			if ( ! $service_id || ! in_array( $service_id, $this->service_ids, true ) ) {

117-129: Quantity lookup must resolve the actual child input

Hard-coding _3 for the quantity input breaks as soon as the field is reordered or uses the Quantity child. That causes us to undercount bookings and miss the cap. Resolve the quantity child and fall back to the legacy index only if needed.

-			$quantity = rgpost( 'input_' . (int) $field->id . '_3' );
-			$quantity = $quantity === null || $quantity === '' ? 1 : max( 1, (int) $quantity );
+			$quantity_field = $children['quantity'] ?? null;
+			$quantity_value = $quantity_field ? $this->get_posted_value( (int) $quantity_field->id ) : null;
+			if ( $quantity_value === null ) {
+				$quantity_value = rgpost( 'input_' . (int) $field->id . '_3' );
+			}
+			$quantity = $quantity_value === null || $quantity_value === '' ? 1 : max( 1, (int) $quantity_value );

@spivurno
Copy link
Contributor

@SebastianWiz Strong! How hard/possible would it be to also prevent the day from being selected at all once the limit is reached? I wouldn't sink too much more time into this, just curious if it's an option?

@SebastianWiz SebastianWiz marked this pull request as draft September 29, 2025 13:46
@SebastianWiz
Copy link
Contributor Author

@spivurno Thank you! I've been exploring this and I think we could hook into the REST API endpoint that provides calendar availability to mark days as unavailable when they hit the limit. That way we could prevent the days from being selectable in the first place rather than showing a validation error after submission. That would definitely be better UX for sure.

Should hopefully have an update to the snippet soon!

@SebastianWiz SebastianWiz marked this pull request as ready for review October 9, 2025 06:55
@SebastianWiz
Copy link
Contributor Author

Update: I rewrote the snippet to hook straight into the availability REST endpoint so days gets blocked from the calendar once they hit the daily limit

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

2 participants