๐ Booking and confirmation of a rental
The booking and confirmation of a rental is a process that involves the following steps:
- The user selects a rental and prepopulate dates in a specific rental page.
- The user click on the "Book" button.
Routesโ
Lets view the routes in config/routes.yaml
:
# book a rental
rental_book:
path: /rental/book/{rentalId}
controller: App\Controller\RentalController::book
methods: [POST]
Controllerโ
The controller is located at src/Controller/RentalController.php
. Lets add the book
method:
/**
* Book action to book a rental
*
* @param Request $request - The request object
* @param string $id - The rental id
*
* @return Response - The response object
*/
#[Route('/rental/book/{rentalId}', name: 'rental_book', methods: ['POST'])]
public function book(Request $request, $rentalId): Response
{
// Fetch the rental by id
$rental = $this->_documentManager->getRepository(Rental::class)->find($rentalId);
// If the rental is not found, throw a 404 exception
if (! $rental) {
throw $this->createNotFoundException('rental not found');
}
// Get the start and end dates from the request
$startDateInput = $request->request->get('startDate');
$endDateInput = $request->request->get('endDate');
$startDate = $startDateInput ? new DateTime($startDateInput) : null;
$endDate = $endDateInput ? new DateTime($endDateInput) : null;
// Calculate total price based on night cost and number of days
$interval = $startDate->diff($endDate);
$days = $interval->days;
$totalPrice = $days * $rental->nightCost;
// Calculate new availability based on the booking dates
$newAvailability = $rental->calcAvailabilitySlots($startDate, $endDate, $rental->availability->toArray());
// Create and persist the booking
$booking = new Booking();
$booking->rental = $rental;
$booking->rentalName = $rental->name;
$booking->startDate = DateTimeImmutable::createFromMutable($startDate);
$booking->endDate = DateTimeImmutable::createFromMutable($endDate);
$booking->totalCost = (int)$totalPrice;
$rental->availability = $newAvailability;
// Persist the booking and rental
$this->_documentManager->persist($booking);
$this->_documentManager->persist($rental);
$this->_documentManager->flush();
// Redirect to a confirmation page or show confirmation message
return $this->render(
'rental/confirmation.html.twig', [
'rental' => $rental,
'booking' => $booking,
'totalPrice' => $totalPrice,
]
);
}
This function does the following:
- Fetch the rental by id
- Get the start and end dates from the request
- Calculate total price based on night cost and number of days
- Calculate new availability based on the booking dates
This helper function is used to calculate the new availability based on the booking dates, and diverge the availability slots:
Exampleโ
Booking "Rental 1" for dates 2024-01-02 to 2024-01-05:
{
rental_name : "Rental 1",
...
availability : [
{ "start_date" : "2024-01-01",
"end_date" : "2024-01-10"
},
{ "start_date" : "2024-01-21",
"end_date" : "2025-01-01"
}
]
}
The availability slots are diverged into 3 slots:
- 2024-01-01 until 2024-01-02
- 2024-01-05 until 2024-01-10
- 2025-01-21 until 2025-01-01
Lets add the helper function to the controller: src/Document/Rental.php
:
/**
* Function : calcAvailabilitySlots
*
* This function is responsible for calculating the
* availability slots based on the booking start and end dates.
*
* @param DateTime $bookingStart - The booking start date
* @param DateTime $bookingEnd - The booking end date
* @param array $availability - The availability array
*
* @return ArrayCollection - The new availability slots
*/
public function calcAvailabilitySlots(DateTime $bookingStart,
DateTime $bookingEnd,
array $availability
): ArrayCollection {
// Create a new ArrayCollection to store the new availability
$newAvailability = new ArrayCollection();
// Loop through each period in the availability to calculate the new availability
foreach ($availability as $period) {
$periodStart = $period->startDate;
$periodEnd = $period->endDate;
// Booking is entirely before this period
if ($bookingEnd < $periodStart) {
$newAvailability->add($period);
continue;
}
// Booking is entirely after this period
if ($bookingStart > $periodEnd) {
$newAvailability->add($period);
continue;
}
// Booking starts before the period and ends within it
if ($bookingStart <= $periodStart && $bookingEnd < $periodEnd) {
$newPeriod = new Availability();
$newPeriod->startDate = $bookingEnd->modify('+1 day');
$newPeriod->endDate = $periodEnd;
$newAvailability->add($newPeriod);
continue;
}
// Booking starts during the period and ends after it
if ($bookingStart > $periodStart && $bookingEnd >= $periodEnd) {
$newPeriod = new Availability();
$newPeriod->startDate= $periodStart;
$newPeriod->endDate = $bookingStart->modify('-1 day');
$newAvailability->add($newPeriod);
continue;
}
// Booking is entirely within the period
if ($bookingStart > $periodStart && $bookingEnd < $periodEnd) {
$newPeriod1 = new Availability();
$newPeriod1->startDate = $periodStart;
$newPeriod1->endDate = $bookingStart->modify('-1 day');
$newAvailability->add($newPeriod1);
$newPeriod2 = new Availability();
$newPeriod2->startDate = $bookingEnd->modify('+1 day');
$newPeriod2->endDate = $periodEnd;
$newAvailability->add($newPeriod2);
continue;
}
// Booking covers the entire period
// Do not add the period to newAvailability (effectively removing it)
}
return $newAvailability;
}
- Create and persist the booking
- Persist the booking and rental
Finally, the function redirects to a confirmation page or shows a confirmation message.
Confirmation Pageโ
The confirmation page is located at templates/rental/confirmation.html.twig
. This page shows the booking details and the total price.
{# templates/rental/confirmation.html.twig #}
{% extends 'base.html.twig' %}
{% block title %}Booking Confirmation{% endblock %}
{% block body %}
<div class="container">
<h2>Booking Confirmation</h2>
<p>Rental: {{ rental.name }}</p>
<p>Location: {{ rental.location }}</p>
<p>Check-in Date: {{ booking.startDate|date('Y-m-d') }}</p>
<p>Check-out Date: {{ booking.endDate|date('Y-m-d') }}</p>
<p>Total Price: {{ totalPrice }}$</p>
<a href="{{ path('rental_index') }}" class="btn btn-success my-3">Back</a>
</div>
{% endblock %}
Lets test the booking processโ
- Search a rental for a specific date
- Navigate to the rental page and click on the "Book" button
Now try to find the same location for those dates and see that no rental is available.