- Added Symfony Validation Lib. - Updated API DTOs to include validation constraints.
This commit is contained in:
@@ -5,7 +5,6 @@ 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;
|
||||
@@ -18,8 +17,6 @@ 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;
|
||||
@@ -51,10 +48,6 @@ class BookingController extends AbstractController
|
||||
#[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');
|
||||
@@ -87,18 +80,12 @@ class BookingController extends AbstractController
|
||||
)
|
||||
)]
|
||||
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 {
|
||||
|
||||
@@ -4,7 +4,6 @@ namespace App\Controller;
|
||||
|
||||
use App\ApiErrorException;
|
||||
use App\DTO\ApiErrorMessage;
|
||||
use App\Repository\FareTableRepository;
|
||||
use App\Repository\RoomAvailabilityRepository;
|
||||
use App\Repository\RoomCategoryRepository;
|
||||
use App\Request\SearchAvailabilityPeriodRequest;
|
||||
@@ -14,12 +13,10 @@ use App\Response\SearchPricesForCategoryResponse;
|
||||
use App\Service\PricesService;
|
||||
use Nelmio\ApiDocBundle\Annotation\Model;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Attribute\Route;
|
||||
use OpenApi\Attributes as OA;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpKernel\Attribute\MapRequestPayload;
|
||||
use Symfony\Component\HttpKernel\Exception\HttpException;
|
||||
|
||||
class SearchRoomsController extends AbstractController
|
||||
{
|
||||
@@ -50,15 +47,9 @@ class SearchRoomsController extends AbstractController
|
||||
)
|
||||
)]
|
||||
public function getAvailabilityForPeriod(
|
||||
Request $request,
|
||||
RoomAvailabilityRepository $repoAvailability,
|
||||
#[MapRequestPayload()] SearchAvailabilityPeriodRequest $searchRequest
|
||||
): Response {
|
||||
if (\is_null($searchRequest)) {
|
||||
throw new ApiErrorException(
|
||||
new ApiErrorMessage(400, ApiErrorMessage::TYPE_INVALID_REQUEST_BODY_FORMAT)
|
||||
);
|
||||
}
|
||||
): JsonResponse {
|
||||
|
||||
if ($searchRequest->startDate >= $searchRequest->endDate) {
|
||||
throw new ApiErrorException(
|
||||
@@ -78,13 +69,9 @@ class SearchRoomsController extends AbstractController
|
||||
#[Route('/api/search/availabilityDetails', name: 'app_search_availabilityDetails', methods: ["POST"], format: "json")]
|
||||
#[OA\Tag(name: "Searchs")]
|
||||
public function getAvailabilityForPeriodDetails(
|
||||
Request $request,
|
||||
RoomAvailabilityRepository $repoAvailability,
|
||||
#[MapRequestPayload()] SearchAvailabilityPeriodRequest $searchRequest
|
||||
): Response {
|
||||
if (\is_null($searchRequest)) {
|
||||
throw new \Exception('Invalid Payload', 400);
|
||||
}
|
||||
): JsonResponse {
|
||||
|
||||
$rs = $repoAvailability->getAvailabilityForPeriodDetails($searchRequest->startDate, $searchRequest->endDate);
|
||||
|
||||
@@ -119,16 +106,10 @@ class SearchRoomsController extends AbstractController
|
||||
)
|
||||
)]
|
||||
public function getPricesForCategoryOnPeriod(
|
||||
Request $request,
|
||||
RoomCategoryRepository $repoCat,
|
||||
PricesService $priceService,
|
||||
#[MapRequestPayload()] SearchPricesForCategoryRequest $searchRequest
|
||||
): Response {
|
||||
if (\is_null($searchRequest)) {
|
||||
throw new ApiErrorException(
|
||||
new ApiErrorMessage(400, ApiErrorMessage::TYPE_INVALID_REQUEST_BODY_FORMAT)
|
||||
);
|
||||
}
|
||||
): JsonResponse {
|
||||
|
||||
$roomCategory = $repoCat->find($searchRequest->roomCategory);
|
||||
if ($roomCategory === null) {
|
||||
@@ -153,19 +134,4 @@ class SearchRoomsController extends AbstractController
|
||||
|
||||
return $this->json(["results" => $rs]);
|
||||
}
|
||||
|
||||
#[Route('/debug/search', name: 'app_search_faresDebug')]
|
||||
public function faresDebug(Request $request, PricesService $priceService, FareTableRepository $repoFareTable, RoomCategoryRepository $repoCat): Response
|
||||
{
|
||||
$fareTable[] = $repoFareTable->find(1);
|
||||
$fareTable[] = $repoFareTable->find(2);
|
||||
$cat = $repoCat->find(4);
|
||||
|
||||
$startDate = new \DateTime("2024-04-27");
|
||||
$endDate = new \DateTime("2024-05-02");
|
||||
|
||||
$rs = $priceService->getFinalPricesForSearch($cat, $startDate, $endDate);
|
||||
|
||||
return $this->json($rs);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ namespace App\Entity;
|
||||
use App\Repository\BookingCustomerRepository;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Symfony\Component\Serializer\Annotation\Ignore;
|
||||
use Symfony\Component\Validator\Constraints as Assert;
|
||||
|
||||
#[ORM\Entity(repositoryClass: BookingCustomerRepository::class)]
|
||||
class BookingCustomer
|
||||
@@ -21,15 +22,20 @@ class BookingCustomer
|
||||
private ?Booking $booking = null;
|
||||
|
||||
#[ORM\Column(length: 64)]
|
||||
#[Assert\NotBlank]
|
||||
private ?string $name = null;
|
||||
|
||||
#[ORM\Column(length: 255)]
|
||||
#[Assert\NotBlank]
|
||||
private ?string $surname = null;
|
||||
|
||||
#[ORM\Column(length: 255)]
|
||||
#[Assert\NotBlank]
|
||||
#[Assert\Email]
|
||||
private ?string $email = null;
|
||||
|
||||
#[ORM\Column(length: 255)]
|
||||
#[Assert\NotBlank]
|
||||
private ?string $phoneNumber = null;
|
||||
|
||||
public function getId(): ?int
|
||||
|
||||
@@ -10,6 +10,7 @@ use Symfony\Component\HttpKernel\Event\ExceptionEvent;
|
||||
use Symfony\Component\HttpKernel\KernelEvents;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
|
||||
use Symfony\Component\Validator\Exception\ValidationFailedException;
|
||||
|
||||
class ApiExceptionSubscriber implements EventSubscriberInterface
|
||||
{
|
||||
@@ -34,7 +35,25 @@ class ApiExceptionSubscriber implements EventSubscriberInterface
|
||||
* low-level exception, which should *not* be exposed
|
||||
*/
|
||||
if ($e instanceof HttpExceptionInterface) {
|
||||
$apiErrorMsg->setExtraData('detail', $e->getMessage());
|
||||
//Check if it's a Validation error.
|
||||
// When using MapRequestPayload() ValidationErrors throw HttpExceptions.
|
||||
$prev = $e->getPrevious();
|
||||
if ($prev instanceof ValidationFailedException) {
|
||||
//Get validation error details.
|
||||
$errorList = [];
|
||||
$violationList = $prev->getViolations();
|
||||
foreach ($violationList as $violation) {
|
||||
$errorList[] = [
|
||||
'field' => $violation->getPropertyPath(),
|
||||
'message' => $violation->getMessage(),
|
||||
];
|
||||
}
|
||||
$apiErrorMsg->setExtraData('detail', "Validation Error. See errorList for details");
|
||||
$apiErrorMsg->setExtraData('errorList', $errorList);
|
||||
} else {
|
||||
//Get exception error message.
|
||||
$apiErrorMsg->setExtraData('detail', $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,19 +3,26 @@
|
||||
namespace App\Request;
|
||||
|
||||
use App\Entity\BookingCustomer;
|
||||
use DateTimeInterface;
|
||||
use OpenApi\Attributes as OA;
|
||||
use Symfony\Component\Validator\Constraints as Assert;
|
||||
|
||||
class BookingCreateRequest
|
||||
{
|
||||
#[OA\Property(description: 'Booking Arrival date')]
|
||||
public \DateTime $arrivalDate;
|
||||
#[Assert\NotBlank]
|
||||
#[Assert\GreaterThanOrEqual(value: 'today')]
|
||||
public DateTimeInterface $arrivalDate;
|
||||
|
||||
#[OA\Property(description: 'Booking departure date')]
|
||||
public \DateTime $departureDate;
|
||||
#[Assert\NotBlank]
|
||||
public DateTimeInterface $departureDate;
|
||||
|
||||
#[OA\Property(
|
||||
description: 'Booking customer details',
|
||||
)]
|
||||
#[Assert\NotBlank]
|
||||
#[Assert\Valid]
|
||||
public BookingCustomer $customerDetails;
|
||||
|
||||
/**
|
||||
@@ -24,5 +31,7 @@ class BookingCreateRequest
|
||||
#[OA\Property(
|
||||
description: 'Rooms to book',
|
||||
)]
|
||||
#[Assert\NotBlank]
|
||||
#[Assert\Valid]
|
||||
public array $rooms;
|
||||
}
|
||||
|
||||
@@ -3,15 +3,21 @@
|
||||
namespace App\Request;
|
||||
|
||||
use OpenApi\Attributes as OA;
|
||||
use Symfony\Component\Validator\Constraints as Assert;
|
||||
|
||||
class BookingCreateRoomItem
|
||||
{
|
||||
#[OA\Property(description: 'Room category ID')]
|
||||
#[Assert\NotBlank]
|
||||
public int $roomCategory;
|
||||
|
||||
#[OA\Property(description: 'Guest number for the room')]
|
||||
#[Assert\NotBlank]
|
||||
#[Assert\GreaterThan(value: 0)]
|
||||
public int $guestNumber;
|
||||
|
||||
#[OA\Property(description: 'Room amount')]
|
||||
#[Assert\NotBlank]
|
||||
#[Assert\GreaterThan(value: 0)]
|
||||
public int $amount;
|
||||
}
|
||||
|
||||
@@ -3,12 +3,16 @@
|
||||
namespace App\Request;
|
||||
|
||||
use OpenApi\Attributes as OA;
|
||||
use Symfony\Component\Validator\Constraints as Assert;
|
||||
|
||||
class BookingDetailsRequest
|
||||
{
|
||||
#[OA\Property(description: 'Booking reference number')]
|
||||
#[Assert\NotBlank]
|
||||
public string $refNumber;
|
||||
|
||||
#[OA\Property(maxLength: 255, description: 'Email associated to the booking')]
|
||||
#[Assert\NotBlank]
|
||||
#[Assert\Email]
|
||||
public string $email;
|
||||
}
|
||||
|
||||
@@ -2,13 +2,17 @@
|
||||
|
||||
namespace App\Request;
|
||||
|
||||
use DateTimeInterface;
|
||||
use OpenApi\Attributes as OA;
|
||||
use Symfony\Component\Validator\Constraints as Assert;
|
||||
|
||||
class SearchAvailabilityPeriodRequest
|
||||
{
|
||||
#[OA\Property(description: 'Period start date')]
|
||||
public \DateTime $startDate;
|
||||
|
||||
#[Assert\NotBlank]
|
||||
public DateTimeInterface $startDate;
|
||||
|
||||
#[OA\Property(description: 'Period end date')]
|
||||
public \DateTime $endDate;
|
||||
#[Assert\NotBlank]
|
||||
public DateTimeInterface $endDate;
|
||||
}
|
||||
|
||||
@@ -2,16 +2,21 @@
|
||||
|
||||
namespace App\Request;
|
||||
|
||||
use DateTimeInterface;
|
||||
use OpenApi\Attributes as OA;
|
||||
use Symfony\Component\Validator\Constraints as Assert;
|
||||
|
||||
class SearchPricesForCategoryRequest
|
||||
{
|
||||
#[OA\Property(description: 'Room category ID')]
|
||||
#[Assert\NotBlank]
|
||||
public int $roomCategory;
|
||||
|
||||
#[OA\Property(description: 'Period start date')]
|
||||
public \DateTime $startDate;
|
||||
#[Assert\NotBlank]
|
||||
public DateTimeInterface $startDate;
|
||||
|
||||
#[OA\Property(description: 'Period end date')]
|
||||
public \DateTime $endDate;
|
||||
#[Assert\NotBlank]
|
||||
public DateTimeInterface $endDate;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user