Skip to content

Instantly share code, notes, and snippets.

@jthurteau
Created August 28, 2023 13:20
Show Gist options
  • Save jthurteau/0eae9425d93d3a0d164d9c0557d7efa8 to your computer and use it in GitHub Desktop.
Save jthurteau/0eae9425d93d3a0d164d9c0557d7efa8 to your computer and use it in GitHub Desktop.
30 (X) Reservations in 30 (Y) Days Algorithm
<?php
function checkUserBookingRules($bookingData, $username)
{
/* $bookingData is a new or modified booking request */
$existingBooking = key_exists('code', $bookingData) ? $bookingData['code'] : null;
/* ... other rule checks ... */
$startWindow = /* $Y days before booking date */;
$windowLength = /* (2 * $Y) + how far out the user can book + 2 extra days */;
$spanDayBookings = $this->getUserBookings($username, $startWindow, $windowLength);
if ($existingBooking) {
/* remove existing booking being modified from $spanDayBookings */
}
$spanDayBookings = self::indexBookingsByDate(
self::filterBookingsByStatus(
$spanDayBookings,
[/*confirmed booking status IDs*/]
)
);
$nextAllowed = $this->getNextAllowedDate($spanDayBookings, $bookingData);
if ($nextAllowed != $bookingData['date']) {
/*reservation is declined with helpful information including $nextAllowed if applicable*/
}
/* otherwise reservation passes X reservations in Y days check */
/* ... */
}
function getNextAllowedDate($existingBookings, $newBooking)
{
/* ... */
$bookingDate = /* validated version of $newBooking['date'] */;
$scanResults = /* converts date indexed array of bookings to date indexed array of booking counts */;
$scanTalley = [];
$spanTotals = []; /* how many reservations are within $Y days either direction of the date */
/* these blocks loops over each date, sum the reservations across spans of $Y within $scanTalley into $spanTotals, then handle the trailing $Y days at the end of $scanTalley */
foreach($scanResults as $scanDate => $scanCount) {
$scanTalley[$scanDate] = $scanCount;
if (count($scanTalley) >= $Y) {
reset($scanTalley);
$completedTallyDate = key($scanTalley);
$spanTotals[$completedTallyDate] = array_sum($scanTalley);
unset($scanTalley[$completedTallyDate]);
}
}
foreach($scanTalley as $scanDate => $unused) {
$spanTotals[$scanDate] = array_sum($scanTalley);
unset($scanTalley[$scanDate]);
}
/* loop through every span of $Y days in the totals, pick the largest number of bookings in that range, if that date is the booking date or later and the booking count less than the max allowed, return that as the next date allowed. this ensures adding the reservation doesn't violate the limit for any given span */
$spanRange = [];
foreach($spanTotals as $scanDate => $scanCount) {
$spanRange[] = $scanCount;
if (count($spanRange) > $Y) {
array_shift($spanRange);
}
if ($spanDate < $bookingDate) {
continue;
}
if(max($spanRange) < $X) {
/* return the earliest date the user can book */
return $scanDate;
}
}
/* if we arrive here, no date in the user’s booking window will satisfy the booking limit */
return null;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment