From c96b525aa46e88d5c9cda2d0ee2e41ebfdc6c010 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Fromme?= Date: Tue, 14 Jan 2025 12:41:40 +0100 Subject: [PATCH] feat: API endpoint to load travel data by code --- src/BusProNet/Model/CrmSelection.php | 9 +++++ src/BusProNet/Model/CrmSelectionGroup.php | 8 +++++ src/BusProNet/Model/Room.php | 2 +- src/BusProNet/XmlLoader/HotelXmlLoader.php | 26 +++++++-------- src/BusProNet/XmlLoader/TravelXmlLoader.php | 21 ++++++++++-- src/Controller/Api/TravelController.php | 37 ++++++++++++++++++--- 6 files changed, 82 insertions(+), 21 deletions(-) diff --git a/src/BusProNet/Model/CrmSelection.php b/src/BusProNet/Model/CrmSelection.php index 2d9642f..4e889ee 100644 --- a/src/BusProNet/Model/CrmSelection.php +++ b/src/BusProNet/Model/CrmSelection.php @@ -2,10 +2,19 @@ namespace App\BusProNet\Model; +use Symfony\Component\Serializer\Attribute\Groups; + class CrmSelection { + #[Groups(['api:single'])] public ?int $id = null; + + #[Groups(['api:single'])] public ?string $label = null; + + #[Groups(['api:single'])] public bool $mutable = false; + + #[Groups(['api:single'])] public bool $selected = false; } \ No newline at end of file diff --git a/src/BusProNet/Model/CrmSelectionGroup.php b/src/BusProNet/Model/CrmSelectionGroup.php index 10c986b..e6826d6 100644 --- a/src/BusProNet/Model/CrmSelectionGroup.php +++ b/src/BusProNet/Model/CrmSelectionGroup.php @@ -2,6 +2,8 @@ namespace App\BusProNet\Model; +use Symfony\Component\Serializer\Attribute\Groups; + class CrmSelectionGroup { /** @@ -16,8 +18,13 @@ class CrmSelectionGroup 86 => 'Programm', ]; + #[Groups(['api:single'])] public ?int $id = null; + + #[Groups(['api:single'])] public ?string $label = null; + + #[Groups(['api:single'])] public ?array $selections = null; public function getMutableSelections(): array @@ -27,6 +34,7 @@ class CrmSelectionGroup }); } + #[Groups(['api:single'])] public function isVisible(): bool { return 0 < count($this->getMutableSelections()); diff --git a/src/BusProNet/Model/Room.php b/src/BusProNet/Model/Room.php index 0a862e1..816fc40 100644 --- a/src/BusProNet/Model/Room.php +++ b/src/BusProNet/Model/Room.php @@ -49,7 +49,7 @@ class Room #[Groups(['api:list', 'api:single'])] public ?int $available = null; - #[Groups(['api:list', 'api:single'])] + #[Groups(['booking'])] public ?string $board = null; #[Groups(['booking'])] diff --git a/src/BusProNet/XmlLoader/HotelXmlLoader.php b/src/BusProNet/XmlLoader/HotelXmlLoader.php index 2399425..c94a20e 100644 --- a/src/BusProNet/XmlLoader/HotelXmlLoader.php +++ b/src/BusProNet/XmlLoader/HotelXmlLoader.php @@ -31,6 +31,19 @@ class HotelXmlLoader extends AbstractXmlLoader } } + public function mapCodeToId(string $hotelCode, ?string $filename = 'hotel.xml'): ?int + { + $hotels = $this->loadAll($filename); + + foreach ($hotels as $hotel) { + if ($hotelCode === $hotel->code) { + return $hotel->id; + } + } + + return null; + } + public function loadById(int $id, ?string $filename = 'hotel.xml'): ?Hotel { $hotels = $this->loadAll($filename); @@ -38,19 +51,6 @@ class HotelXmlLoader extends AbstractXmlLoader return $hotels[$id] ?? null; } - public function loadByCode(string $code, ?string $filename = 'hotel.xml'): ?Hotel - { - $hotels = $this->loadAll($filename); - - foreach ($hotels as $hotel) { - if ($code === $hotel->code) { - return $hotel; - } - } - - return null; - } - private function loadXml(?string $filename = 'hotel.xml'): \SimpleXMLElement { $filepath = $this->xmlPath.'/'.$filename; diff --git a/src/BusProNet/XmlLoader/TravelXmlLoader.php b/src/BusProNet/XmlLoader/TravelXmlLoader.php index 2084e60..9c006ea 100644 --- a/src/BusProNet/XmlLoader/TravelXmlLoader.php +++ b/src/BusProNet/XmlLoader/TravelXmlLoader.php @@ -25,7 +25,7 @@ class TravelXmlLoader extends AbstractXmlLoader parent::__construct($cache, $xmlPath); } - public function generateMapping(): array + public function generateFilesMap(): array { return $this->cache->get('bpn_travels_mapping', function (ItemInterface $item) { $item->expiresAfter(3600); @@ -46,6 +46,7 @@ class TravelXmlLoader extends AbstractXmlLoader $mapping[$travelId] = [ 'id' => (int) $attributes['idbuspro'], + 'code' => (string) $attributes['code'], 'label' => (string) $travelXml->text, 'dateFrom' => $this->stringToDate((string) $attributes['termin']), 'dateTo' => $this->stringToDate((string) $attributes['bis']), @@ -58,6 +59,7 @@ class TravelXmlLoader extends AbstractXmlLoader $mapping[$travelId]['hotels'][$hotelId] = [ 'id' => $hotelId, + 'code' => (string) $hotelXml->attributes()['code'], 'label' => $hotels[$hotelId]->name, ]; } @@ -68,13 +70,26 @@ class TravelXmlLoader extends AbstractXmlLoader }); } + public function mapCodeToId(string $travelCode): ?int + { + $mapping = $this->generateFilesMap(); + + $travelCodes = array_column($mapping, 'code', 'id'); + + if (false === $travelId = array_search($travelCode, $travelCodes)) { + return null; + } + + return $travelId; + } + public function loadById(int $travelId, ?int $hotelId = null, ?string $filename = null): ?Travel { if (null !== $filename) { return $this->loadXml($travelId, $hotelId, $filename); } - $mapping = $this->generateMapping(); + $mapping = $this->generateFilesMap(); if (false === isset($mapping[$travelId])) { return null; @@ -107,7 +122,7 @@ class TravelXmlLoader extends AbstractXmlLoader $travel = new Travel(); $travel->id = (int) $attributes['idbuspro']; $travel->label = (string) $xml->text; - $travel->hotelId = $hotelId; + $travel->hotelId = (int) $hotelXml->attributes()['idbuspro']; $travel->dateFrom = $this->stringToDate((string) $attributes['termin']); $travel->dateTo = $this->stringToDate((string) $attributes['bis']); $travel->code = (string) $attributes['code']; diff --git a/src/Controller/Api/TravelController.php b/src/Controller/Api/TravelController.php index 7de0d07..7373f66 100644 --- a/src/Controller/Api/TravelController.php +++ b/src/Controller/Api/TravelController.php @@ -2,6 +2,7 @@ namespace App\Controller\Api; +use App\BusProNet\Model\Travel; use App\BusProNet\XmlLoader\HotelXmlLoader; use App\BusProNet\XmlLoader\PickupXmlLoader; use App\BusProNet\XmlLoader\TravelXmlLoader; @@ -27,19 +28,47 @@ class TravelController extends AbstractController #[Route(path: '/travels', name: 'api_travel_mapping')] public function mapping(): JsonResponse { - $mapping = $this->travelXmlLoader->generateMapping(); + $mapping = $this->travelXmlLoader->generateFilesMap(); return $this->json($mapping); } #[Route( path: '/travels/{travelId}/{hotelId}', - name: 'api_travel_single', + name: 'api_travel_single_id', requirements: ['travelId' => '\d+', 'hotelId' => '\d+'] )] - public function single(int $travelId, ?int $hotelId = null): JsonResponse + public function singleById(int $travelId, ?int $hotelId = null): JsonResponse + { + $travel = $this->loadCached($travelId, $hotelId); + + return $this->json($travel, Response::HTTP_OK, [], ['groups' => ['api:list', 'api:single']]); + } + + #[Route( + path: '/travels/{travelCode}/{hotelCode}', + name: 'api_travel_single_code', + )] + public function singleByCode(string $travelCode, ?string $hotelCode = null): JsonResponse + { + $travelCode = str_replace('-', '/', $travelCode); + + $travelId = $this->travelXmlLoader->mapCodeToId($travelCode); + $hotelId = $hotelCode ? $this->hotelXmlLoader->mapCodeToId($travelCode) : null; + + if (null === $travelId) { + return new JsonResponse(['message' => 'Not found'], Response::HTTP_NOT_FOUND); + } + + $travel = $this->loadCached($travelId, $hotelId); + + return $this->json($travel, Response::HTTP_OK, [], ['groups' => ['api:list', 'api:single']]); + } + + private function loadCached(int $travelId, ?int $hotelId = null): ?Travel { $cacheKey = sprintf('bpn_travel_%d_%d', $travelId, $hotelId ?? 0); + try { $travel = $this->cache->get($cacheKey, function (ItemInterface $item) use ($travelId, $hotelId) { $item->expiresAfter(60); @@ -59,6 +88,6 @@ class TravelController extends AbstractController $travel = null; } - return $this->json($travel, Response::HTTP_OK, [], ['groups' => ['api:list', 'api:single']]); + return $travel; } } \ No newline at end of file