Files
SomniaRooms/src/Controller/BookingController.php
T

206 lines
7.6 KiB
PHP

<?php
namespace App\Controller;
use App\ApiErrorException;
use App\DTO\ApiErrorMessage;
use App\Entity\Booking;
use App\Entity\BookingCustomer;
use App\Entity\BookingRoom;
use App\Repository\BookingRepository;
use App\Repository\RoomCategoryRepository;
use App\Repository\RoomAvailabilityRepository;
use App\Request\BookingCreateRequest;
use App\Request\BookingDetailsRequest;
use App\Response\BookingCreateResponse;
use App\Service\PricesService;
use Doctrine\ORM\EntityManagerInterface;
use Nelmio\ApiDocBundle\Annotation\Model;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\HttpException;
use Symfony\Component\Routing\Attribute\Route;
use OpenApi\Attributes as OA;
use Symfony\Component\HttpKernel\Attribute\MapRequestPayload;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
class BookingController extends AbstractController
{
/**
* Get booking details
*/
#[Route('/api/booking/details', name: 'app_booking_details', methods: ["POST"], format: "json")]
#[OA\Tag(name: "Booking")]
#[OA\Response(
response: 200,
description: 'Returns booking details',
content: new OA\JsonContent(
type: 'object',
ref: new Model(type: Booking::class),
)
)]
#[OA\RequestBody(
content: new OA\JsonContent(
type: 'object',
ref: new Model(type: BookingDetailsRequest::class),
)
)]
public function getBookingDetails(
BookingRepository $repoBooking,
#[MapRequestPayload()] BookingDetailsRequest $detailsRequest
): JsonResponse {
if (\is_null($detailsRequest)) {
throw new ApiErrorException(new ApiErrorMessage(400, ApiErrorMessage::TYPE_INVALID_REQUEST_BODY_FORMAT));
}
$selectedBooking = $repoBooking->findOneBy(["refNumber" => $detailsRequest->refNumber]);
if (\is_null($selectedBooking)) {
throw new NotFoundHttpException('Booking not found');
}
if ($selectedBooking->getBookingCustomer()->getEmail() != $detailsRequest->email) {
throw new NotFoundHttpException('Booking not found');
}
return $this->json($selectedBooking);
}
/**
* Create a new Booking.
*/
#[Route('/api/booking/create', name: 'app_booking_create', methods: ["POST"], format: "json")]
#[OA\Tag(name: "Booking")]
#[OA\Response(
response: 200,
description: 'Returns booking details',
content: new OA\JsonContent(
type: 'object',
ref: new Model(type: BookingCreateResponse::class)
)
)]
#[OA\RequestBody(
content: new OA\JsonContent(
type: 'object',
ref: new Model(type: BookingCreateRequest::class),
)
)]
public function newBooking(
Request $request,
EntityManagerInterface $entityManager,
RoomCategoryRepository $repoCat,
RoomAvailabilityRepository $repoAvailability,
PricesService $priceSrv,
#[MapRequestPayload()] BookingCreateRequest $createRequest
): JsonResponse {
if (\is_null($createRequest)) {
throw new ApiErrorException(
new ApiErrorMessage(400, ApiErrorMessage::TYPE_INVALID_REQUEST_BODY_FORMAT)
);
}
$booking = new Booking();
try {
if ($createRequest->arrivalDate >= $createRequest->departureDate) {
throw new ApiErrorException(
new ApiErrorMessage(422, ApiErrorMessage::TYPE_INVALID_DATE),
'Arrival cant be before departure',
);
}
$entityManager->beginTransaction();
$currentTime = new \DateTime("now");
// Create booking from request data.
$booking->setBookingTime($currentTime);
$booking->setArrivalDate($createRequest->arrivalDate);
$booking->setDepartureDate($createRequest->departureDate);
$booking->setBookingCustomer($createRequest->customerDetails);
//TODOME: Improve refNumber generation.
$highest_id = $entityManager->createQueryBuilder()
->select('MAX(e.id)')
->from('App\Entity\Booking', 'e')
->getQuery()
->getSingleScalarResult();
$booking->setRefNumber($currentTime->format("Y") . $highest_id + 1);
$entityManager->persist($booking);
if (\count($createRequest->rooms) == 0) {
throw new ApiErrorException(
new ApiErrorMessage(422, ApiErrorMessage::TYPE_VALIDATION_ERROR),
'Booking must contain rooms',
);
}
// Create each room.
foreach ($createRequest->rooms as $roomItem) {
$room = new BookingRoom();
$room->setBooking($booking);
//Get Room category.
$roomCategory = $repoCat->find($roomItem->roomCategory);
if ($roomCategory == null) {
throw new ApiErrorException(
new ApiErrorMessage(422, ApiErrorMessage::TYPE_VALIDATION_ERROR),
'Invalid RoomCategory: ' . $roomItem->roomCategory,
);
}
//Check room availability.
$amountAvailable = $repoAvailability->getAvailabilityForCategoryInPeriod($roomCategory, $createRequest->arrivalDate, $createRequest->departureDate);
$selectedAmount = $roomItem->amount;
$selectedGuestAmount = $roomItem->guestNumber;
if ($amountAvailable == null || $selectedAmount > $amountAvailable) {
throw new ApiErrorException(
new ApiErrorMessage(400, ApiErrorMessage::TYPE_LOGICAL_ERROR),
'Not enought rooms available for category: ' . $roomItem->roomCategory,
);
}
$room->setRoomCategory($roomCategory);
$room->setGuestNumber($selectedGuestAmount);
$room->setAmount($selectedAmount);
//Get Price for period.
$priceList = $priceSrv->getFinalPricesForSearch($roomCategory, $createRequest->arrivalDate, $createRequest->departureDate);
$priceFound = false;
foreach ($priceList as $key => $value) {
if ($key == $selectedGuestAmount) {
$room->setRoomPrice($value);
$priceFound = true;
break;
}
}
if (!$priceFound) {
throw new ApiErrorException(
new ApiErrorMessage(422, ApiErrorMessage::TYPE_LOGICAL_ERROR),
'Price not found for category: ' . $roomCategory->getId() . ', guests: ' . $selectedGuestAmount,
);
}
$entityManager->persist($room);
//If booking is nice, let's update current availability.
$repoAvailability->updateAvailabilityForRoomCategoryOnPeriod($roomCategory, $createRequest->arrivalDate, $createRequest->departureDate, $selectedAmount, true); //Update using "diff" mode.
}
$entityManager->commit();
$entityManager->flush();
} catch (\Throwable $th) {
$entityManager->rollback();
throw $th;
}
$results = ["refNumber" => $booking->getRefNumber()];
return $this->json($results);
}
}