From 5a74247a45a0dd4a6fd9c09967514ddb38fd1358 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Fromme?= Date: Wed, 5 Jul 2023 15:27:28 +0200 Subject: [PATCH] Feat: Implement additional caches for expensive db queries --- .../Classes/Service/ContingentDataService.php | 73 ++++++++++++------- .../Classes/Service/DateService.php | 38 ++++++++-- .../Classes/Service/ProductDataService.php | 41 ++++++++--- .../ep_products/Configuration/Services.yaml | 16 +++- 4 files changed, 117 insertions(+), 51 deletions(-) diff --git a/public/typo3conf/ext/ep_products/Classes/Service/ContingentDataService.php b/public/typo3conf/ext/ep_products/Classes/Service/ContingentDataService.php index f98a1eb7..5a3a6985 100644 --- a/public/typo3conf/ext/ep_products/Classes/Service/ContingentDataService.php +++ b/public/typo3conf/ext/ep_products/Classes/Service/ContingentDataService.php @@ -32,20 +32,30 @@ use EP\EpProducts\Domain\Model\Product; use EP\EpProducts\Domain\Repository\ContingentRepository; use EP\EpProducts\Utility\BookingUrlUtility; use League\Period\Period; +use TYPO3\CMS\Core\Cache\Frontend\FrontendInterface; class ContingentDataService { + public const CACHE_TTL = 60 * 60; + /** * @var ContingentRepository */ protected $contingentRepository; /** - * @param ContingentRepository $contingentRepository + * @var FrontendInterface */ - public function __construct(ContingentRepository $contingentRepository) + private $cache; + + /** + * @param ContingentRepository $contingentRepository + * @param FrontendInterface $cache + */ + public function __construct(ContingentRepository $contingentRepository, FrontendInterface $cache) { $this->contingentRepository = $contingentRepository; + $this->cache = $cache; } /** @@ -56,18 +66,21 @@ class ContingentDataService */ public function getAvailableContingents(Hotel $hotel, Product $product) { - $data = $this->contingentRepository->getAvailableContingents($hotel, $product); + $key = 'contingents_'.sha1($hotel->getUid().$product->getUid()); - $contingents = []; + if (false === $contingents = $this->cache->get($key)) { + $data = $this->contingentRepository->getAvailableContingents($hotel, $product); + $contingents = []; + foreach ($data as $row) { + $contingents[$row['date']] = [ + 'date' => $row['date'], + 'minNights' => $row['minNights'], + 'total' => $row['total'], + 'capacity' => $row['capacity'], + ]; + } - foreach ($data as $row) - { - $contingents[$row['date']] = [ - 'date' => $row['date'], - 'minNights' => $row['minNights'], - 'total' => $row['total'], - 'capacity' => $row['capacity'], - ]; + $this->cache->set($key, $contingents, [], self::CACHE_TTL); } return $contingents; @@ -89,28 +102,32 @@ class ContingentDataService Product $product, $template = 'base' ) { - $data = $this->contingentRepository->getAvailableRooms($dateFrom, $dateTo, $hotel, $product); - $period = new Period($dateFrom, $dateTo); - $nights = $period->dateInterval()->format('%a'); + $key = 'rooms_'.sha1($dateFrom.$dateTo.$hotel->getUid().$product->getUid()); - $rooms = []; + if (false === $rooms = $this->cache->get($key)) { + $data = $this->contingentRepository->getAvailableRooms($dateFrom, $dateTo, $hotel, $product); + $period = new Period($dateFrom, $dateTo); + $nights = $period->dateInterval()->format('%a'); + $rooms = []; + foreach ($data as $row) { + $row['bookingUrl'] = BookingUrlUtility::generateUrl([ + 'bookingId' => $row['busProId'], + 'hotelId' => $row['hotelBusProId'], + 'dateEnd' => $dateTo, + 'template' => $template, + ]); - foreach ($data as $row) { - $row['bookingUrl'] = BookingUrlUtility::generateUrl([ - 'bookingId' => $row['busProId'], - 'hotelId' => $row['hotelBusProId'], - 'dateEnd' => $dateTo, - 'template' => $template, - ]); + $row['summer'] = $row['season'] === 's'; - $row['summer'] = $row['season'] === 's'; + $row['servicesIncluded'] = unserialize($row['servicesIncluded'], ['allowed_classes' => false]); + $row['servicesOptional'] = unserialize($row['servicesOptional'], ['allowed_classes' => false]); - $row['servicesIncluded'] = unserialize($row['servicesIncluded'], ['allowed_classes' => false]); - $row['servicesOptional'] = unserialize($row['servicesOptional'], ['allowed_classes' => false]); + $row['priceForSelection'] = $row['price'] + ($nights - $row['minNights']) * $row['additionalNightPrice']; - $row['priceForSelection'] = $row['price'] + ($nights - $row['minNights']) * $row['additionalNightPrice']; + $rooms[] = $row; + } - $rooms[] = $row; + $this->cache->set($key, $rooms, [], self::CACHE_TTL); } return $rooms; diff --git a/public/typo3conf/ext/ep_products/Classes/Service/DateService.php b/public/typo3conf/ext/ep_products/Classes/Service/DateService.php index db9223c3..fdc0722a 100644 --- a/public/typo3conf/ext/ep_products/Classes/Service/DateService.php +++ b/public/typo3conf/ext/ep_products/Classes/Service/DateService.php @@ -33,12 +33,15 @@ use EP\EpProducts\Domain\Repository\DateRepository; use EP\EpProducts\Domain\Repository\ProductRepository; use EP\EpProducts\Utility\BookingUrlUtility; use Symfony\Component\OptionsResolver\OptionsResolver; +use TYPO3\CMS\Core\Cache\Frontend\FrontendInterface; use TYPO3\CMS\Core\SingletonInterface; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Extbase\Mvc\Web\Routing\UriBuilder; class DateService implements SingletonInterface { + public const CACHE_TTL = 60 * 60; + /** * @var ProductRepository */ @@ -59,6 +62,11 @@ class DateService implements SingletonInterface */ protected $uriBuilder; + /** + * @var FrontendInterface + */ + protected $cache; + /** * @var string[] */ @@ -76,19 +84,22 @@ class DateService implements SingletonInterface * @param DateRepository $dateRepository * @param FilterService $filterService * @param UriBuilder $uriBuilder + * @param FrontendInterface $cache */ public function __construct ( ProductRepository $productRepository, DateRepository $dateRepository, FilterService $filterService, - UriBuilder $uriBuilder + UriBuilder $uriBuilder, + FrontendInterface $cache ) { $this->productRepository = $productRepository; $this->filterService = $filterService; $this->dateRepository = $dateRepository; $this->uriBuilder = $uriBuilder; + $this->cache = $cache; } /** @@ -114,8 +125,13 @@ class DateService implements SingletonInterface $filterSettings = $this->filterService->getNormalizedFilterSettings(); } + $key = 'pricetable_'.sha1($product->getUid().$hotel->getUid().serialize($filterSettings).$date ? $date->getUid():0); + if (false === $priceTableData = $this->cache->get($key)) { + $priceTableData = $this->productRepository->getPricetable($product, $hotel, $filterSettings, $date); + $this->cache->set($key, $priceTableData, ['ep_products'], self::CACHE_TTL); + } + $nonBookableDateUids = GeneralUtility::trimExplode(',', $product->getNonBookableDates(), true); - $priceTableData = $this->productRepository->getPricetable($product, $hotel, $filterSettings, $date); return $this->preprocessPriceTable([ 'priceTableData' => $priceTableData, @@ -152,11 +168,13 @@ class DateService implements SingletonInterface $filterSettings = $this->filterService->getNormalizedFilterSettings(); } - return $this->productRepository->getAvailableDates( - $product, - $hotel, - $filterSettings - ); + $key = 'datestable_'.sha1($product->getUid().$hotel->getUid().serialize($filterSettings)); + if (false === $datesTableData = $this->cache->get($key)) { + $datesTableData = $this->productRepository->getAvailableDates($product, $hotel, $filterSettings); + $this->cache->set($key, $datesTableData, ['ep_products'], self::CACHE_TTL); + } + + return $datesTableData; } /** @@ -172,7 +190,11 @@ class DateService implements SingletonInterface public function getEventPriceTable(Product $product, int $maxPax = 99, string $template = 'base'): array { - $priceTableData = $this->productRepository->getEventPriceTable($product); + $key = 'pricetable_'.sha1($product->getUid()); + if (false === $priceTableData = $this->cache->get($key)) { + $priceTableData = $this->productRepository->getEventPriceTable($product); + $this->cache->set($key, $priceTableData, ['ep_products'], self::CACHE_TTL); + } return $this->preprocessEventPriceTable([ 'priceTableData' => $priceTableData, diff --git a/public/typo3conf/ext/ep_products/Classes/Service/ProductDataService.php b/public/typo3conf/ext/ep_products/Classes/Service/ProductDataService.php index 4c78fac1..01b1edf9 100644 --- a/public/typo3conf/ext/ep_products/Classes/Service/ProductDataService.php +++ b/public/typo3conf/ext/ep_products/Classes/Service/ProductDataService.php @@ -29,10 +29,12 @@ namespace EP\EpProducts\Service; use EP\EpProducts\Domain\Model\Hotel; use EP\EpProducts\Domain\Repository\HotelRepository; use EP\EpProducts\Domain\Repository\ProductRepository; +use TYPO3\CMS\Core\Cache\Frontend\FrontendInterface; use TYPO3\CMS\Core\SingletonInterface; class ProductDataService implements SingletonInterface { + public const CACHE_TTL = 60 * 60; /** * @var ProductRepository @@ -54,6 +56,11 @@ class ProductDataService implements SingletonInterface */ protected $hotelImageService; + /** + * @var FrontendInterface + */ + protected $cache; + /** * @param ProductRepository $productRepository * @param HotelRepository $hotelRepository @@ -64,12 +71,14 @@ class ProductDataService implements SingletonInterface ProductRepository $productRepository, HotelRepository $hotelRepository, HotelImageService $hotelImageService, - ProductImageService $productImageService + ProductImageService $productImageService, + FrontendInterface $cache ) { $this->productRepository = $productRepository; $this->hotelRepository = $hotelRepository; $this->productImageService = $productImageService; $this->hotelImageService = $hotelImageService; + $this->cache = $cache; } /** @@ -79,8 +88,13 @@ class ProductDataService implements SingletonInterface */ public function getTeaser(array $filterSettings): ?array { - $teaserData = $this->productRepository->getTeasers($filterSettings); - $teasers = $this->preprocessTeasergroup($teaserData); + $key = 'teaser_group_'.sha1(serialize($filterSettings)); + + if (false === $teasers = $this->cache->get($key)) { + $teaserData = $this->productRepository->getTeasers($filterSettings); + $teasers = $this->preprocessTeasergroup($teaserData); + $this->cache->set($key, $teasers, ['ep_products'], self::CACHE_TTL); + } if (0 === count($teasers)) { return null; @@ -98,18 +112,27 @@ class ProductDataService implements SingletonInterface */ public function getTeasergroup(array $filterSettings, array $excludedConceptUids = [], int $limit = 0): array { - $teaserData = $this->productRepository->getTeasers($filterSettings, $excludedConceptUids); + $key = 'teaser_group_'.sha1(serialize($filterSettings).serialize($excludedConceptUids)); - return $this->preprocessTeasergroup($teaserData, $limit); + if (false === $teasers = $this->cache->get($key)) { + $teaserData = $this->productRepository->getTeasers($filterSettings, $excludedConceptUids); + $teasers = $this->preprocessTeasergroup($teaserData); + $this->cache->set($key, $teasers, ['ep_products'], self::CACHE_TTL); + } + + if ($limit > 0) { + return \array_slice($teasers, 0, $limit); + } + + return $teasers; } /** * @param array $teaserData - * @param int $limit * * @return array */ - public function preprocessTeasergroup(array $teaserData, int $limit = 0): array + public function preprocessTeasergroup(array $teaserData): array { $data = []; @@ -158,10 +181,6 @@ class ProductDataService implements SingletonInterface } } - if ($limit > 0) { - return \array_slice($data, 0, $limit); - } - return $data; } diff --git a/public/typo3conf/ext/ep_products/Configuration/Services.yaml b/public/typo3conf/ext/ep_products/Configuration/Services.yaml index 4c8bd98a..f6854165 100644 --- a/public/typo3conf/ext/ep_products/Configuration/Services.yaml +++ b/public/typo3conf/ext/ep_products/Configuration/Services.yaml @@ -22,12 +22,20 @@ services: EP\EpProducts\Service\HotelImageService: arguments: - $fileRepository: '@TYPO3\CMS\Core\Resource\FileRepository' - $imageService: '@TYPO3\CMS\Extbase\Service\ImageService' $cache: '@cache.ep_cache' EP\EpProducts\Service\ProductImageService: arguments: - $fileRepository: '@TYPO3\CMS\Core\Resource\FileRepository' - $imageService: '@TYPO3\CMS\Extbase\Service\ImageService' + $cache: '@cache.ep_cache' + + EP\EpProducts\Service\DateService: + arguments: + $cache: '@cache.ep_cache' + + EP\EpProducts\Service\ProductDataService: + arguments: + $cache: '@cache.ep_cache' + + EP\EpProducts\Service\ContingentDataService: + arguments: $cache: '@cache.ep_cache'