Streamline fetching of filter options

This commit is contained in:
Björn Fromme
2021-10-27 10:44:08 +02:00
parent 01d19d900e
commit ba667684c1
13 changed files with 33 additions and 409 deletions
@@ -74,7 +74,7 @@ class AjaxFilterPanelController extends ActionController
->searchResultService
->getResult($filterSettings, $excludedConceptUids);
$filterOptions = $this->filterService->collectFilterOptionsFromResult($searchResult);
$filterOptions = $this->filterService->collectFilterOptionsFromSearchResult($searchResult);
$resultsUri = $this->uriBuilder
->reset()
@@ -98,7 +98,7 @@ class AjaxSearchController extends ActionController
$organic = false;
}
$filterOptions = $this->filterService->collectFilterOptionsFromResult($searchResult);
$filterOptions = $this->filterService->collectFilterOptionsFromSearchResult($searchResult);
$referringPageUri = $this
->uriBuilder
@@ -100,7 +100,6 @@ class SearchController extends ActionController
$organic = true;
$forcedConceptUids = GeneralUtility::trimExplode(',', $this->settings['forcedConceptUids'], true);
$excludedConceptUids = GeneralUtility::trimExplode(',', $this->settings['excludedConceptUids']);
$excludedRegionUids = GeneralUtility::trimExplode(',', $this->settings['excludedRegionUids']);
if (!empty($filterSettings['dateFrom']) || !empty($filterSettings['dateTo'])) {
$dateSelected = true;
@@ -125,7 +124,7 @@ class SearchController extends ActionController
$organic = false;
}
$filterOptions = $this->filterService->collectFilterOptionsFromResult($searchResult);
$filterOptions = $this->filterService->collectFilterOptionsFromSearchResult($searchResult);
$referringPageUri = $this
->uriBuilder
@@ -39,7 +39,7 @@ class SearchbarController extends ActionController
use RequestArgumentTypeConversionTrait;
/**
* @var \EP\EpProducts\Service\FilterService
* @var FilterService
*/
protected $filterService;
@@ -69,15 +69,14 @@ class SearchbarController extends ActionController
public function indexAction(array $searchParams)
{
$forcedConceptUids = GeneralUtility::trimExplode(',', $this->settings['forcedConceptUids'], true);
$filterSettings = $this->filterService->getFilterSettingsFromSearchParams($searchParams);
$filterSettings = $this->filterService->getFilterSettingsFromSearchParams($searchParams, $forcedConceptUids);
$excludedConceptUids = GeneralUtility::trimExplode(',', $this->settings['excludedConceptUids']);
$excludedRegionUids = GeneralUtility::trimExplode(',', $this->settings['excludedRegionUids']);
$filterOptions = $this->filterService->getFilterOptions(
$filterSettings,
$excludedConceptUids,
$excludedRegionUids,
$forcedConceptUids
);
$searchResult = $this
->searchResultService
->getResult($filterSettings, $excludedConceptUids);
$filterOptions = $this->filterService->collectFilterOptionsFromSearchResult($searchResult);
$this->view->assignMultiple([
'filterOptions' => $filterOptions,
@@ -71,63 +71,6 @@ class DateRepository extends AbstractRepository
return [$dateRangeFrom, $dateRangeTo];
}
public function getFilterOptions(
array $filterSettings,
array $excludedConceptUids = [],
array $excludedRegionUids = [],
array $forcedConceptUids = []
) {
$qb = $this->getDbConnection()->createQueryBuilder();
$query = $qb
->select(
'date.concept as conceptUid', 'date.concept_name as conceptName', 'date.concept_sorting as conceptSorting',
'date.concept_code as conceptCode', 'date.min_price as minPrice', 'date.date_start as dateStart', 'date.date_end as dateEnd',
'date.country as countryUid','date.country_name as countryName', 'date.country_code as countryCode',
'date.region as regionUid', 'date.region_name as regionName',
'date.nights as nights', 'date.board as boardType', 'date.board_bus_pro_id as boardUid',
'date.bus_available as busAvailable',
'date.hotel_type as hotelType',
'room.type as roomType'
)
->from('tx_epproducts_domain_model_date', 'date')
->innerJoin('date', 'tx_epproducts_domain_model_room', 'room', 'room.date = date.uid')
->where('date.date_start >= NOW()')
->andWhere('date.product_searchable = 1')
->andWhere('date.hotel_type > 0')
->orderBy('conceptSorting')
->addOrderBy('countryName')
->addOrderBy('regionName')
->addOrderBy('nights')
->addOrderBy('roomType')
;
if (count($excludedConceptUids) > 0) {
$query
->andWhere('date.concept NOT IN (:excludedConceptUids)')
->setParameter('excludedConceptUids', $excludedConceptUids, Connection::PARAM_INT_ARRAY)
;
}
if (count($excludedRegionUids) > 0) {
$query
->andWhere('date.region NOT IN (:excludedRegionUids)')
->setParameter('excludedRegionUids', $excludedRegionUids, Connection::PARAM_INT_ARRAY)
;
}
if (count($forcedConceptUids) > 0) {
$query
->andWhere('date.concept IN (:forcedConceptUids)')
->setParameter('forcedConceptUids', $forcedConceptUids, Connection::PARAM_INT_ARRAY)
;
}
$this->addFiltersettingsConditions($query, $filterSettings);
return $query->execute()->fetchAllAssociative();
}
/**
* @param string $search
* @param array $filterSettings
@@ -27,6 +27,8 @@ namespace EP\EpProducts\Traits;
* This copyright notice MUST APPEAR in all copies of the script!
***************************************************************/
use TYPO3\CMS\Core\Cache\Frontend\FrontendInterface;
use TYPO3\CMS\Core\Imaging\ImageManipulation\Area;
use TYPO3\CMS\Core\Imaging\ImageManipulation\CropVariantCollection;
use TYPO3\CMS\Core\Resource\FileReference;
use TYPO3\CMS\Core\Resource\FileRepository;
@@ -35,12 +37,7 @@ use TYPO3\CMS\Extbase\Service\ImageService;
trait ImageServiceTrait
{
/**
* @var array
*/
protected static $cache = [];
/**
* @var \TYPO3\CMS\Core\Resource\FileRepository
* @var FileRepository
*/
protected $fileRepository;
@@ -50,42 +47,38 @@ trait ImageServiceTrait
protected $imageService;
/**
* @param FileRepository $fileRepository
* @param ImageService $imageService
* @var FrontendInterface
*/
public function __construct(FileRepository $fileRepository, ImageService $imageService)
protected $cache;
public function __construct(FileRepository $fileRepository, ImageService $imageService, FrontendInterface $cache)
{
$this->fileRepository = $fileRepository;
$this->imageService = $imageService;
$this->cache = $cache;
}
/**
* @param int $uid
* @return array
*/
public function getImages($uid)
public function getImages($uid): array
{
if (!isset(static::$cache[$uid])) {
$key = $this->table.$uid;
if (false === ($value = $this->cache->get($key))) {
$original = $this->fileRepository->findByRelation(
$this->table,
$this->field,
$uid
);
$resized = $this->generateResizedImages($original);
static::$cache[$uid] = [
$value = [
'original' => $original,
'resized' => $resized,
];
$this->cache->set($key, $value);
}
return static::$cache[$uid];
return $value;
}
/**
* @param array $images
* @return array
*/
public function generateResizedImages($images)
public function generateResizedImages($images): array
{
$resized = [];
foreach ($this->imageSizes as $version => $instructions)
@@ -101,20 +94,17 @@ trait ImageServiceTrait
];
}
}
return $resized;
}
/**
* @param FileReference $image
* @param array $processingInstructions
* @return null|\TYPO3\CMS\Core\Imaging\ImageManipulation\Area
*/
protected function getCroppingInfo(FileReference $image, array $processingInstructions)
protected function getCroppingInfo(FileReference $image, array $processingInstructions): ?Area
{
$cropString = $image->getProperty('crop');
$cropVariantCollection = CropVariantCollection::create((string) $cropString);
$cropVariant = $processingInstructions['cropVariant'] ?: 'default';
$cropArea = $cropVariantCollection->getCropArea($cropVariant);
return $cropArea->isEmpty() ? null : $cropArea->makeAbsoluteBasedOnFile($image);
}
}
@@ -39,11 +39,6 @@ class FilterService implements SingletonInterface
public const DURATION_SHORT = 4;
public const DURATION_LONG = 5;
/**
* @var array
*/
static protected $cache = [];
/**
* @var \EP\EpProducts\Domain\Repository\DateRepository
*/
@@ -87,9 +82,9 @@ class FilterService implements SingletonInterface
return $filterSettings;
}
public function getFilterSettingsFromSearchParams(array $searchParams): array
public function getFilterSettingsFromSearchParams(array $searchParams, array $forcedConceptUids = []): array
{
$filterSettings = $this->getNormalizedFilterSettings();
$filterSettings = $this->getNormalizedFilterSettings([], $forcedConceptUids);
if ($searchParams['destinationType'] && $searchParams['destinationUid']) {
switch ($searchParams['destinationType']) {
@@ -164,27 +159,7 @@ class FilterService implements SingletonInterface
return $filterSettings;
}
public function getFilterOptions
(
array $filterSettings,
array $excludedConceptUids = [],
array $excludedRegionUids = [],
array $forcedConceptUids = []
): FilterOptions {
$filterOptionsData = $this->dateRepository->getFilterOptions(
$filterSettings,
$excludedConceptUids,
$excludedRegionUids,
$forcedConceptUids
);
if (count($filterOptionsData) === 0) {
return new FilterOptions();
}
return $this->preprocessFilterOptions($filterOptionsData);
}
public function collectFilterOptionsFromResult(SearchResult $searchResult): FilterOptions
public function collectFilterOptionsFromSearchResult(SearchResult $searchResult): FilterOptions
{
$priceRanges = [];
$busAvailable = false;
@@ -302,133 +277,6 @@ class FilterService implements SingletonInterface
return $filterOptions;
}
public function preprocessFilterOptions(array $filterOptionsData): FilterOptions
{
$priceRanges = [];
$busAvailable = false;
$nights = [];
$concepts = [];
$destinations = [];
$hotelTypes = [];
$roomTypes = [];
$boardTypes = [];
$dateMin = $dateMax = null;
foreach ($filterOptionsData as $row) {
// Date
if (null === $dateMin || $row['dateStart'] < $dateMin) {
$dateMin = $row['dateStart'];
}
if (null === $dateMax || $row['dateEnd'] > $dateMax) {
$dateMax = $row['dateEnd'];
}
// Bus
if ((bool) $row['busAvailable'] === true) {
$busAvailable = true;
}
// Nights
$duration = (int) $row['nights'] <= 4 ? self::DURATION_SHORT : self::DURATION_LONG;
if (!\in_array($duration, $nights, false)) {
$nights[] = $duration;
}
// Concepts
if (!array_key_exists($row['conceptUid'], $concepts)) {
$concepts[$row['conceptSorting']] = [
'uid' => $row['conceptUid'],
'name' => $row['conceptName'],
'code' => $row['conceptCode'],
];
}
// Destinations
if (!array_key_exists($row['countryUid'], $destinations)) {
$destinations[$row['countryUid']] = [
'country' => [
'uid' => $row['countryUid'],
'label' => $row['countryName'],
'code' => $row['countryCode'],
],
'regions' => [],
];
}
if (!array_key_exists($row['regionUid'], $destinations[$row['countryUid']]['regions'])) {
$destinations[$row['countryUid']]['regions'][$row['regionUid']] = [
'uid' => $row['regionUid'],
'label' => $row['regionName'],
];
}
// Hotel types
if (!\in_array($row['hotelType'], $hotelTypes, false)) {
$hotelTypes[] = $row['hotelType'];
}
// Room types
if (!\in_array($row['roomType'], $roomTypes, false)) {
$roomTypes[] = $row['roomType'];
}
// Board types
$boardType = [
'uid' => $row['boardUid'],
'label' => $row['board'],
];
if (!\in_array($boardType, $boardTypes, false)) {
$boardTypes[] = $boardType;
}
// Price range
foreach (FilterOptions::$priceRangeOptions as $index => $priceRange) {
if (array_key_exists($index, $priceRanges)) {
continue;
}
[$min, $max] = $priceRange;
if ($row['minPrice'] >= $min && $row['minPrice'] < $max) {
$range = FilterOptions::$priceRangeOptions[$index];
$priceRanges[$index] = [
'uid' => $index,
'min' => $range[0],
'max' => $range[1],
'formatted' => $this->formatPriceRange($range[0], $range[1]),
];
}
}
}
foreach ($destinations as $key => $value)
{
usort($value['regions'], function ($a, $b) {
return strcmp($a['label'], $b['label']);
});
$destinations[$key] = $value;
}
sort($nights);
ksort($priceRanges);
usort($boardTypes, function ($a, $b) {
return strcmp($a['label'], $b['label']);
});
$filterOptions = new FilterOptions();
$filterOptions->setPriceRanges($priceRanges);
$filterOptions->setDateFrom($dateMin);
$filterOptions->setDateTo($dateMax);
$filterOptions->setConcepts($concepts);
$filterOptions->setDestinations($destinations);
$filterOptions->setHotelTypes($hotelTypes);
$filterOptions->setRoomTypes($roomTypes);
$filterOptions->setBoardTypes($boardTypes);
$filterOptions->setBusAvailable($busAvailable);
$filterOptions->setNights($nights);
return $filterOptions;
}
public function formatPriceRange(int $min, int $max): string
{
if ($min === 1) {
@@ -1,67 +0,0 @@
<?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\SingletonInterface;
use TYPO3\CMS\Extbase\Service\ImageService;
class FlagIconService implements SingletonInterface
{
const FLAGICON_PATH = 'EXT:ep_theme/Resources/Public/images/%s.png';
/**
* @var ImageService
*/
protected $imageService;
/**
* @var array
*/
protected static $cache = [];
/**
* @param ImageService $imageService
*/
public function __construct(ImageService $imageService)
{
$this->imageService = $imageService;
}
/**
* @param string $code
* @return string
*/
public function getImage($code)
{
if (!isset(self::$cache[$code])) {
$src = sprintf(self::FLAGICON_PATH, strtolower($code));
$image = $this->imageService->getImage($src, null, false);
self::$cache[$code] = $this->imageService->getImageUri($image, true);
}
return self::$cache[$code];
}
}
@@ -49,26 +49,14 @@ class SearchResultService implements SingletonInterface
*/
protected $hotelImageService;
/**
* @var FlagIconService
*/
protected $flagIconService;
/**
* @param ProductRepository $productRepository
* @param HotelImageService $hotelImageService
* @param FlagIconService $flagIconService
*/
public function __construct
(
ProductRepository $productRepository,
HotelImageService $hotelImageService,
FlagIconService $flagIconService
)
public function __construct(ProductRepository $productRepository, HotelImageService $hotelImageService)
{
$this->productRepository = $productRepository;
$this->hotelImageService = $hotelImageService;
$this->flagIconService = $flagIconService;
}
public function getNormalizedSearchParams(array $searchParams = []): array
@@ -147,7 +135,6 @@ class SearchResultService implements SingletonInterface
'uid' => $row['countryUid'],
'code' => $row['countryCode'],
'name' => $row['countryName'],
'flag' => $this->flagIconService->getImage($row['countryCode']),
],
'region' => [
'uid' => $row['regionUid'],
@@ -1,75 +0,0 @@
<?php
namespace EP\EpTheme\ViewHelpers;
/***************************************************************
*
* 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 EP\EpProducts\Domain\Model\Country;
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic;
class FlagiconSourceViewHelper extends AbstractViewHelper
{
use CompileWithRenderStatic;
public function initializeArguments()
{
parent::initializeArguments();
$this->registerArgument('countryCode', 'string', 'Country code to return a flag icon source for.');
$this->registerArgument('country', 'mixed', 'Country entity to return a flag icon source for.');
}
/**
* @param array $arguments
* @param \Closure $renderChildrenClosure
* @param RenderingContextInterface $renderingContext
* @return mixed
*/
public static function renderStatic(
array $arguments,
\Closure $renderChildrenClosure,
RenderingContextInterface $renderingContext
)
{
$code = null;
if (\is_object($arguments['country']) && $arguments['country'] instanceof Country) {
/** @var \EP\EpProducts\Domain\Model\Country $country */
$country = $arguments['country'];
$code = strtolower($country->getCode());
} elseif (!empty($arguments['countryCode'])) {
$code = strtolower($arguments['countryCode']);
}
if ($code !== null) {
return 'EXT:ep_theme/Resources/Public/images/' . $code . '.png';
}
return 'EXT:ep_theme/Resources/Public/images/gb.png';
}
}