From 83b3cbea4d6049c094046d4d16d627dabe3684f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Fromme?= Date: Thu, 16 Jul 2026 08:41:11 +0200 Subject: [PATCH] feat: extended api endpoint for hotel data consumed by myep --- ...uctApiMiddleware.php => ApiMiddleware.php} | 144 ++++++++++++++++-- .../Classes/Service/MyEpImageService.php | 63 ++++++++ .../Configuration/RequestMiddlewares.php | 2 +- .../ep_products/Configuration/Services.yaml | 28 ++++ 4 files changed, 224 insertions(+), 13 deletions(-) rename public/typo3conf/ext/ep_products/Classes/Middleware/{ProductApiMiddleware.php => ApiMiddleware.php} (60%) create mode 100644 public/typo3conf/ext/ep_products/Classes/Service/MyEpImageService.php diff --git a/public/typo3conf/ext/ep_products/Classes/Middleware/ProductApiMiddleware.php b/public/typo3conf/ext/ep_products/Classes/Middleware/ApiMiddleware.php similarity index 60% rename from public/typo3conf/ext/ep_products/Classes/Middleware/ProductApiMiddleware.php rename to public/typo3conf/ext/ep_products/Classes/Middleware/ApiMiddleware.php index 108a9113..1d5bf5cd 100644 --- a/public/typo3conf/ext/ep_products/Classes/Middleware/ProductApiMiddleware.php +++ b/public/typo3conf/ext/ep_products/Classes/Middleware/ApiMiddleware.php @@ -2,18 +2,58 @@ namespace EP\EpProducts\Middleware; -use EP\EpProducts\Service\HotelImageService; -use EP\EpProducts\Service\ProductImageService; +use EP\EpProducts\Service\MyEpImageService; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Server\MiddlewareInterface; use Psr\Http\Server\RequestHandlerInterface; use TYPO3\CMS\Core\Database\ConnectionPool; use TYPO3\CMS\Core\Http\JsonResponse; +use TYPO3\CMS\Core\Resource\FileRepository; use TYPO3\CMS\Core\Utility\GeneralUtility; +use TYPO3\CMS\Extbase\Service\ImageService; -class ProductApiMiddleware implements MiddlewareInterface +class ApiMiddleware implements MiddlewareInterface { + /** + * @var MyEpImageService + */ + private $hotelImageService; + + /** + * @var MyEpImageService + */ + private $productImageService; + + /** + * @var MyEpImageService + */ + private $regionImageService; + + /** + * @var FileRepository + */ + private $fileRepository; + + /** + * @var ImageService + */ + private $imageService; + + public function __construct( + MyEpImageService $hotelImageService, + MyEpImageService $productImageService, + MyEpImageService $regionImageService, + FileRepository $fileRepository, + ImageService $imageService + ) { + $this->hotelImageService = $hotelImageService; + $this->productImageService = $productImageService; + $this->regionImageService = $regionImageService; + $this->fileRepository = $fileRepository; + $this->imageService = $imageService; + } + public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface { $path = $request->getUri()->getPath(); @@ -91,15 +131,15 @@ class ProductApiMiddleware implements MiddlewareInterface $data = $queryBuilder ->select( + 'product.uid as productUid', 'product.name as productName', 'product.code as productCode', + 'product.region as regionUid', 'country.name as countryName', - 'region.name as regionName', 'city.name as cityName', ) ->from('tx_epproducts_domain_model_product', 'product') ->leftJoin('product', 'tx_epproducts_domain_model_country', 'country', 'product.country = country.uid') - ->leftJoin('product', 'tx_epproducts_domain_model_region', 'region', 'product.region = region.uid') ->leftJoin('product', 'tx_epproducts_domain_model_city', 'city', 'product.city = city.uid') ->leftJoin('product', 'tx_epproducts_domain_model_matchcode', 'matchcode', 'product.uid = matchcode.entity AND matchcode.entity_type="hotel"') ->where($queryBuilder->expr()->eq('product.code', $queryBuilder->createNamedParameter($productCode))) @@ -113,8 +153,7 @@ class ProductApiMiddleware implements MiddlewareInterface return null; } - $images = GeneralUtility::makeInstance(ProductImageService::class) - ->getImages((int) $data['uid']); + $images = $this->serializeImages($this->productImageService->getImages((int) $data['productUid'])); return [ 'product' => [ @@ -125,9 +164,7 @@ class ProductApiMiddleware implements MiddlewareInterface 'country' => [ 'name' => $data['countryName'], ], - 'region' => [ - 'name' => $data['regionName'], - ], + 'region' => $this->fetchRegionData((int) $data['regionUid']), 'city' => [ 'name' => $data['cityName'], ], @@ -159,6 +196,7 @@ class ProductApiMiddleware implements MiddlewareInterface 'hotel.wifi', 'hotel.distance_bus', 'hotel.distance_lift', + 'hotel.region', ) ->from('tx_epproducts_domain_model_hotel', 'hotel') ->leftJoin('hotel', 'tx_epproducts_domain_model_matchcode', 'alias', 'hotel.uid = alias.entity AND alias.entity_type="hotel"') @@ -177,8 +215,7 @@ class ProductApiMiddleware implements MiddlewareInterface return []; } - $images = GeneralUtility::makeInstance(HotelImageService::class) - ->getImages((int) $hotel['uid']); + $images = $this->serializeImages($this->hotelImageService->getImages((int) $hotel['uid'])); return [ 'name' => $hotel['name'], @@ -201,6 +238,89 @@ class ProductApiMiddleware implements MiddlewareInterface 'distanceBus' => $hotel['distance_bus'], 'distanceLift' => $hotel['distance_lift'], ], + 'region' => $this->fetchRegionData((int) $hotel['region']), ]; } + + private function fetchRegionData(int $regionUid): array + { + if (0 === $regionUid) { + return []; + } + + $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class) + ->getQueryBuilderForTable('tx_epproducts_domain_model_region'); + + $region = $queryBuilder + ->select( + 'region.uid', + 'region.name', + 'region.latitude', + 'region.longitude', + 'region.ski_area', + 'region.ski_area_extended', + 'region.description', + 'region.news', + 'region.length', + 'region.height', + 'region.lifts', + 'webcam.url as webcamUrl', + ) + ->from('tx_epproducts_domain_model_region', 'region') + ->leftJoin('region', 'tx_epproducts_domain_model_webcam', 'webcam', 'region.webcam = webcam.uid') + ->where($queryBuilder->expr()->eq('region.uid', $queryBuilder->createNamedParameter($regionUid, \PDO::PARAM_INT))) + ->setMaxResults(1) + ->execute() + ->fetchAssociative() + ; + + if (false === $region) { + return []; + } + + $images = $this->serializeImages($this->regionImageService->getImages((int) $region['uid'])); + $regionMaps = $this->serializeFileReferences( + $this->fileRepository->findByRelation('tx_epproducts_domain_model_region', 'region_maps', (int) $region['uid']) + ); + + return [ + 'name' => $region['name'], + 'latitude' => $region['latitude'], + 'longitude' => $region['longitude'], + 'webcam' => $region['webcamUrl'], + 'ski_area' => $region['ski_area'], + 'ski_area_extended' => $region['ski_area_extended'], + 'description' => $region['description'], + 'news' => $region['news'], + 'length' => $region['length'], + 'altitude' => $region['height'], + 'lifts' => $region['lifts'], + 'images' => $images, + 'region_maps' => $regionMaps, + ]; + } + + /** + * The 'original' entry of AbstractImageService::getImages() holds raw FileReference objects, + * which are needed by Fluid templates elsewhere but serialize to empty objects in JSON responses. + * We replace it with the same {url, alt, title} shape used for 'region_maps'. + */ + private function serializeImages(array $images): array + { + return [ + 'original' => $this->serializeFileReferences($images['original']), + 'resized' => $images['resized'], + ]; + } + + private function serializeFileReferences(array $files): array + { + return array_map(function ($file) { + return [ + 'url' => $this->imageService->getImageUri($file, true), + 'alt' => $file->getProperty('alternative'), + 'title' => $file->getProperty('title'), + ]; + }, $files); + } } diff --git a/public/typo3conf/ext/ep_products/Classes/Service/MyEpImageService.php b/public/typo3conf/ext/ep_products/Classes/Service/MyEpImageService.php new file mode 100644 index 00000000..59233d82 --- /dev/null +++ b/public/typo3conf/ext/ep_products/Classes/Service/MyEpImageService.php @@ -0,0 +1,63 @@ +, dreipunktnull + * + * All rights reserved + * + * This script is part of the TYPO3 project. The TYPO3 project is + * free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * The GNU General Public License can be found at + * http://www.gnu.org/copyleft/gpl.html. + * + * This script is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * This copyright notice MUST APPEAR in all copies of the script! + ***************************************************************/ + +use TYPO3\CMS\Core\Cache\Frontend\FrontendInterface; +use TYPO3\CMS\Core\Resource\FileRepository; +use TYPO3\CMS\Extbase\Service\ImageService; + +class MyEpImageService extends AbstractImageService +{ + /** + * @var string + */ + protected $field = 'images'; + + /** + * @var array + */ + protected $imageSizes = [ + 's' => [ + 'width' => '480', + 'height' => '360c-100', + ], + 'm' => [ + 'width' => '768', + 'height' => '576c-100', + ], + 'l' => [ + 'width' => '1280', + 'height' => '960c-100', + ] + ]; + + public function __construct(FileRepository $fileRepository, ImageService $imageService, FrontendInterface $cache, string $table = 'tx_epproducts_domain_model_hotel') + { + parent::__construct($fileRepository, $imageService, $cache); + $this->table = $table; + } +} diff --git a/public/typo3conf/ext/ep_products/Configuration/RequestMiddlewares.php b/public/typo3conf/ext/ep_products/Configuration/RequestMiddlewares.php index 0ba3b6ca..48276357 100644 --- a/public/typo3conf/ext/ep_products/Configuration/RequestMiddlewares.php +++ b/public/typo3conf/ext/ep_products/Configuration/RequestMiddlewares.php @@ -12,7 +12,7 @@ return [ ], ], 'ep/productapi' => [ - 'target' => \EP\EpProducts\Middleware\ProductApiMiddleware::class, + 'target' => \EP\EpProducts\Middleware\ApiMiddleware::class, 'before' => [ 'typo3/cms-frontend/page-resolver', ], diff --git a/public/typo3conf/ext/ep_products/Configuration/Services.yaml b/public/typo3conf/ext/ep_products/Configuration/Services.yaml index f146a82c..2d886792 100644 --- a/public/typo3conf/ext/ep_products/Configuration/Services.yaml +++ b/public/typo3conf/ext/ep_products/Configuration/Services.yaml @@ -31,3 +31,31 @@ services: EP\EpProducts\Controller\AjaxCalendarController: arguments: $cache: '@cache.ep_cache' + + EP\EpProducts\Service\MyEpImageService: + arguments: + $cache: '@cache.ep_cache' + + ep_products.image_service.hotel: + class: EP\EpProducts\Service\MyEpImageService + arguments: + $cache: '@cache.ep_cache' + $table: 'tx_epproducts_domain_model_hotel' + + ep_products.image_service.product: + class: EP\EpProducts\Service\MyEpImageService + arguments: + $cache: '@cache.ep_cache' + $table: 'tx_epproducts_domain_model_product' + + ep_products.image_service.region: + class: EP\EpProducts\Service\MyEpImageService + arguments: + $cache: '@cache.ep_cache' + $table: 'tx_epproducts_domain_model_region' + + EP\EpProducts\Middleware\ApiMiddleware: + arguments: + $hotelImageService: '@ep_products.image_service.hotel' + $productImageService: '@ep_products.image_service.product' + $regionImageService: '@ep_products.image_service.region'