Feat: Implement additional caches for expensive db queries

This commit is contained in:
Björn Fromme
2023-07-05 15:28:08 +02:00
parent 4c74a3d0e9
commit 5a74247a45
4 changed files with 117 additions and 51 deletions
@@ -32,20 +32,30 @@ use EP\EpProducts\Domain\Model\Product;
use EP\EpProducts\Domain\Repository\ContingentRepository; use EP\EpProducts\Domain\Repository\ContingentRepository;
use EP\EpProducts\Utility\BookingUrlUtility; use EP\EpProducts\Utility\BookingUrlUtility;
use League\Period\Period; use League\Period\Period;
use TYPO3\CMS\Core\Cache\Frontend\FrontendInterface;
class ContingentDataService class ContingentDataService
{ {
public const CACHE_TTL = 60 * 60;
/** /**
* @var ContingentRepository * @var ContingentRepository
*/ */
protected $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->contingentRepository = $contingentRepository;
$this->cache = $cache;
} }
/** /**
@@ -56,12 +66,12 @@ class ContingentDataService
*/ */
public function getAvailableContingents(Hotel $hotel, Product $product) public function getAvailableContingents(Hotel $hotel, Product $product)
{ {
$key = 'contingents_'.sha1($hotel->getUid().$product->getUid());
if (false === $contingents = $this->cache->get($key)) {
$data = $this->contingentRepository->getAvailableContingents($hotel, $product); $data = $this->contingentRepository->getAvailableContingents($hotel, $product);
$contingents = []; $contingents = [];
foreach ($data as $row) {
foreach ($data as $row)
{
$contingents[$row['date']] = [ $contingents[$row['date']] = [
'date' => $row['date'], 'date' => $row['date'],
'minNights' => $row['minNights'], 'minNights' => $row['minNights'],
@@ -70,6 +80,9 @@ class ContingentDataService
]; ];
} }
$this->cache->set($key, $contingents, [], self::CACHE_TTL);
}
return $contingents; return $contingents;
} }
@@ -89,12 +102,13 @@ class ContingentDataService
Product $product, Product $product,
$template = 'base' $template = 'base'
) { ) {
$key = 'rooms_'.sha1($dateFrom.$dateTo.$hotel->getUid().$product->getUid());
if (false === $rooms = $this->cache->get($key)) {
$data = $this->contingentRepository->getAvailableRooms($dateFrom, $dateTo, $hotel, $product); $data = $this->contingentRepository->getAvailableRooms($dateFrom, $dateTo, $hotel, $product);
$period = new Period($dateFrom, $dateTo); $period = new Period($dateFrom, $dateTo);
$nights = $period->dateInterval()->format('%a'); $nights = $period->dateInterval()->format('%a');
$rooms = []; $rooms = [];
foreach ($data as $row) { foreach ($data as $row) {
$row['bookingUrl'] = BookingUrlUtility::generateUrl([ $row['bookingUrl'] = BookingUrlUtility::generateUrl([
'bookingId' => $row['busProId'], 'bookingId' => $row['busProId'],
@@ -113,6 +127,9 @@ class ContingentDataService
$rooms[] = $row; $rooms[] = $row;
} }
$this->cache->set($key, $rooms, [], self::CACHE_TTL);
}
return $rooms; return $rooms;
} }
} }
@@ -33,12 +33,15 @@ use EP\EpProducts\Domain\Repository\DateRepository;
use EP\EpProducts\Domain\Repository\ProductRepository; use EP\EpProducts\Domain\Repository\ProductRepository;
use EP\EpProducts\Utility\BookingUrlUtility; use EP\EpProducts\Utility\BookingUrlUtility;
use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\OptionsResolver\OptionsResolver;
use TYPO3\CMS\Core\Cache\Frontend\FrontendInterface;
use TYPO3\CMS\Core\SingletonInterface; use TYPO3\CMS\Core\SingletonInterface;
use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Mvc\Web\Routing\UriBuilder; use TYPO3\CMS\Extbase\Mvc\Web\Routing\UriBuilder;
class DateService implements SingletonInterface class DateService implements SingletonInterface
{ {
public const CACHE_TTL = 60 * 60;
/** /**
* @var ProductRepository * @var ProductRepository
*/ */
@@ -59,6 +62,11 @@ class DateService implements SingletonInterface
*/ */
protected $uriBuilder; protected $uriBuilder;
/**
* @var FrontendInterface
*/
protected $cache;
/** /**
* @var string[] * @var string[]
*/ */
@@ -76,19 +84,22 @@ class DateService implements SingletonInterface
* @param DateRepository $dateRepository * @param DateRepository $dateRepository
* @param FilterService $filterService * @param FilterService $filterService
* @param UriBuilder $uriBuilder * @param UriBuilder $uriBuilder
* @param FrontendInterface $cache
*/ */
public function __construct public function __construct
( (
ProductRepository $productRepository, ProductRepository $productRepository,
DateRepository $dateRepository, DateRepository $dateRepository,
FilterService $filterService, FilterService $filterService,
UriBuilder $uriBuilder UriBuilder $uriBuilder,
FrontendInterface $cache
) )
{ {
$this->productRepository = $productRepository; $this->productRepository = $productRepository;
$this->filterService = $filterService; $this->filterService = $filterService;
$this->dateRepository = $dateRepository; $this->dateRepository = $dateRepository;
$this->uriBuilder = $uriBuilder; $this->uriBuilder = $uriBuilder;
$this->cache = $cache;
} }
/** /**
@@ -114,8 +125,13 @@ class DateService implements SingletonInterface
$filterSettings = $this->filterService->getNormalizedFilterSettings(); $filterSettings = $this->filterService->getNormalizedFilterSettings();
} }
$nonBookableDateUids = GeneralUtility::trimExplode(',', $product->getNonBookableDates(), true); $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); $priceTableData = $this->productRepository->getPricetable($product, $hotel, $filterSettings, $date);
$this->cache->set($key, $priceTableData, ['ep_products'], self::CACHE_TTL);
}
$nonBookableDateUids = GeneralUtility::trimExplode(',', $product->getNonBookableDates(), true);
return $this->preprocessPriceTable([ return $this->preprocessPriceTable([
'priceTableData' => $priceTableData, 'priceTableData' => $priceTableData,
@@ -152,11 +168,13 @@ class DateService implements SingletonInterface
$filterSettings = $this->filterService->getNormalizedFilterSettings(); $filterSettings = $this->filterService->getNormalizedFilterSettings();
} }
return $this->productRepository->getAvailableDates( $key = 'datestable_'.sha1($product->getUid().$hotel->getUid().serialize($filterSettings));
$product, if (false === $datesTableData = $this->cache->get($key)) {
$hotel, $datesTableData = $this->productRepository->getAvailableDates($product, $hotel, $filterSettings);
$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 public function getEventPriceTable(Product $product, int $maxPax = 99, string $template = 'base'): array
{ {
$key = 'pricetable_'.sha1($product->getUid());
if (false === $priceTableData = $this->cache->get($key)) {
$priceTableData = $this->productRepository->getEventPriceTable($product); $priceTableData = $this->productRepository->getEventPriceTable($product);
$this->cache->set($key, $priceTableData, ['ep_products'], self::CACHE_TTL);
}
return $this->preprocessEventPriceTable([ return $this->preprocessEventPriceTable([
'priceTableData' => $priceTableData, 'priceTableData' => $priceTableData,
@@ -29,10 +29,12 @@ namespace EP\EpProducts\Service;
use EP\EpProducts\Domain\Model\Hotel; use EP\EpProducts\Domain\Model\Hotel;
use EP\EpProducts\Domain\Repository\HotelRepository; use EP\EpProducts\Domain\Repository\HotelRepository;
use EP\EpProducts\Domain\Repository\ProductRepository; use EP\EpProducts\Domain\Repository\ProductRepository;
use TYPO3\CMS\Core\Cache\Frontend\FrontendInterface;
use TYPO3\CMS\Core\SingletonInterface; use TYPO3\CMS\Core\SingletonInterface;
class ProductDataService implements SingletonInterface class ProductDataService implements SingletonInterface
{ {
public const CACHE_TTL = 60 * 60;
/** /**
* @var ProductRepository * @var ProductRepository
@@ -54,6 +56,11 @@ class ProductDataService implements SingletonInterface
*/ */
protected $hotelImageService; protected $hotelImageService;
/**
* @var FrontendInterface
*/
protected $cache;
/** /**
* @param ProductRepository $productRepository * @param ProductRepository $productRepository
* @param HotelRepository $hotelRepository * @param HotelRepository $hotelRepository
@@ -64,12 +71,14 @@ class ProductDataService implements SingletonInterface
ProductRepository $productRepository, ProductRepository $productRepository,
HotelRepository $hotelRepository, HotelRepository $hotelRepository,
HotelImageService $hotelImageService, HotelImageService $hotelImageService,
ProductImageService $productImageService ProductImageService $productImageService,
FrontendInterface $cache
) { ) {
$this->productRepository = $productRepository; $this->productRepository = $productRepository;
$this->hotelRepository = $hotelRepository; $this->hotelRepository = $hotelRepository;
$this->productImageService = $productImageService; $this->productImageService = $productImageService;
$this->hotelImageService = $hotelImageService; $this->hotelImageService = $hotelImageService;
$this->cache = $cache;
} }
/** /**
@@ -79,8 +88,13 @@ class ProductDataService implements SingletonInterface
*/ */
public function getTeaser(array $filterSettings): ?array public function getTeaser(array $filterSettings): ?array
{ {
$key = 'teaser_group_'.sha1(serialize($filterSettings));
if (false === $teasers = $this->cache->get($key)) {
$teaserData = $this->productRepository->getTeasers($filterSettings); $teaserData = $this->productRepository->getTeasers($filterSettings);
$teasers = $this->preprocessTeasergroup($teaserData); $teasers = $this->preprocessTeasergroup($teaserData);
$this->cache->set($key, $teasers, ['ep_products'], self::CACHE_TTL);
}
if (0 === count($teasers)) { if (0 === count($teasers)) {
return null; return null;
@@ -98,18 +112,27 @@ class ProductDataService implements SingletonInterface
*/ */
public function getTeasergroup(array $filterSettings, array $excludedConceptUids = [], int $limit = 0): array 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 array $teaserData
* @param int $limit
* *
* @return array * @return array
*/ */
public function preprocessTeasergroup(array $teaserData, int $limit = 0): array public function preprocessTeasergroup(array $teaserData): array
{ {
$data = []; $data = [];
@@ -158,10 +181,6 @@ class ProductDataService implements SingletonInterface
} }
} }
if ($limit > 0) {
return \array_slice($data, 0, $limit);
}
return $data; return $data;
} }
@@ -22,12 +22,20 @@ services:
EP\EpProducts\Service\HotelImageService: EP\EpProducts\Service\HotelImageService:
arguments: arguments:
$fileRepository: '@TYPO3\CMS\Core\Resource\FileRepository'
$imageService: '@TYPO3\CMS\Extbase\Service\ImageService'
$cache: '@cache.ep_cache' $cache: '@cache.ep_cache'
EP\EpProducts\Service\ProductImageService: EP\EpProducts\Service\ProductImageService:
arguments: arguments:
$fileRepository: '@TYPO3\CMS\Core\Resource\FileRepository' $cache: '@cache.ep_cache'
$imageService: '@TYPO3\CMS\Extbase\Service\ImageService'
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' $cache: '@cache.ep_cache'