feat: extended api endpoint for hotel data consumed by myep

This commit is contained in:
Björn Fromme
2026-07-16 08:41:11 +02:00
parent 11fda126a8
commit 83b3cbea4d
4 changed files with 224 additions and 13 deletions
@@ -2,18 +2,58 @@
namespace EP\EpProducts\Middleware; namespace EP\EpProducts\Middleware;
use EP\EpProducts\Service\HotelImageService; use EP\EpProducts\Service\MyEpImageService;
use EP\EpProducts\Service\ProductImageService;
use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface; use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface; use Psr\Http\Server\RequestHandlerInterface;
use TYPO3\CMS\Core\Database\ConnectionPool; use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Http\JsonResponse; use TYPO3\CMS\Core\Http\JsonResponse;
use TYPO3\CMS\Core\Resource\FileRepository;
use TYPO3\CMS\Core\Utility\GeneralUtility; 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 public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{ {
$path = $request->getUri()->getPath(); $path = $request->getUri()->getPath();
@@ -91,15 +131,15 @@ class ProductApiMiddleware implements MiddlewareInterface
$data = $queryBuilder $data = $queryBuilder
->select( ->select(
'product.uid as productUid',
'product.name as productName', 'product.name as productName',
'product.code as productCode', 'product.code as productCode',
'product.region as regionUid',
'country.name as countryName', 'country.name as countryName',
'region.name as regionName',
'city.name as cityName', 'city.name as cityName',
) )
->from('tx_epproducts_domain_model_product', 'product') ->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_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_city', 'city', 'product.city = city.uid')
->leftJoin('product', 'tx_epproducts_domain_model_matchcode', 'matchcode', 'product.uid = matchcode.entity AND matchcode.entity_type="hotel"') ->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))) ->where($queryBuilder->expr()->eq('product.code', $queryBuilder->createNamedParameter($productCode)))
@@ -113,8 +153,7 @@ class ProductApiMiddleware implements MiddlewareInterface
return null; return null;
} }
$images = GeneralUtility::makeInstance(ProductImageService::class) $images = $this->serializeImages($this->productImageService->getImages((int) $data['productUid']));
->getImages((int) $data['uid']);
return [ return [
'product' => [ 'product' => [
@@ -125,9 +164,7 @@ class ProductApiMiddleware implements MiddlewareInterface
'country' => [ 'country' => [
'name' => $data['countryName'], 'name' => $data['countryName'],
], ],
'region' => [ 'region' => $this->fetchRegionData((int) $data['regionUid']),
'name' => $data['regionName'],
],
'city' => [ 'city' => [
'name' => $data['cityName'], 'name' => $data['cityName'],
], ],
@@ -159,6 +196,7 @@ class ProductApiMiddleware implements MiddlewareInterface
'hotel.wifi', 'hotel.wifi',
'hotel.distance_bus', 'hotel.distance_bus',
'hotel.distance_lift', 'hotel.distance_lift',
'hotel.region',
) )
->from('tx_epproducts_domain_model_hotel', 'hotel') ->from('tx_epproducts_domain_model_hotel', 'hotel')
->leftJoin('hotel', 'tx_epproducts_domain_model_matchcode', 'alias', 'hotel.uid = alias.entity AND alias.entity_type="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 []; return [];
} }
$images = GeneralUtility::makeInstance(HotelImageService::class) $images = $this->serializeImages($this->hotelImageService->getImages((int) $hotel['uid']));
->getImages((int) $hotel['uid']);
return [ return [
'name' => $hotel['name'], 'name' => $hotel['name'],
@@ -201,6 +238,89 @@ class ProductApiMiddleware implements MiddlewareInterface
'distanceBus' => $hotel['distance_bus'], 'distanceBus' => $hotel['distance_bus'],
'distanceLift' => $hotel['distance_lift'], '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);
}
} }
@@ -0,0 +1,63 @@
<?php
namespace EP\EpProducts\Service;
/***************************************************************
*
* Copyright notice
*
* (c) 2018 Björn Fromme <[email protected]>, 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;
}
}
@@ -12,7 +12,7 @@ return [
], ],
], ],
'ep/productapi' => [ 'ep/productapi' => [
'target' => \EP\EpProducts\Middleware\ProductApiMiddleware::class, 'target' => \EP\EpProducts\Middleware\ApiMiddleware::class,
'before' => [ 'before' => [
'typo3/cms-frontend/page-resolver', 'typo3/cms-frontend/page-resolver',
], ],
@@ -31,3 +31,31 @@ services:
EP\EpProducts\Controller\AjaxCalendarController: EP\EpProducts\Controller\AjaxCalendarController:
arguments: arguments:
$cache: '@cache.ep_cache' $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'