Implement filterable offer list
This commit is contained in:
@@ -0,0 +1,139 @@
|
||||
<?php
|
||||
namespace EP\EpEvents\Controller;
|
||||
|
||||
use EP\EpEvents\Domain\Dto\FilterSettings;
|
||||
use EP\EpEvents\Domain\Model\Offer;
|
||||
use EP\EpEvents\Domain\Repository\OfferRepository;
|
||||
use EP\EpEvents\Service\FilterOptionsCollector;
|
||||
use EP\EpEvents\Service\SectionCssClassService;
|
||||
use EP\EpEvents\Service\SoftHyphenService;
|
||||
use TYPO3\CMS\Core\Imaging\ImageManipulation\CropVariantCollection;
|
||||
use TYPO3\CMS\Core\Resource\FileReference;
|
||||
use TYPO3\CMS\Core\Resource\FileRepository;
|
||||
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
|
||||
use TYPO3\CMS\Extbase\Service\ImageService;
|
||||
|
||||
class AjaxOfferController extends ActionController
|
||||
{
|
||||
/**
|
||||
* @var OfferRepository
|
||||
*/
|
||||
protected $offerRepository;
|
||||
|
||||
/**
|
||||
* @var FileRepository
|
||||
*/
|
||||
protected $fileRepository;
|
||||
|
||||
/**
|
||||
* @var ImageService
|
||||
*/
|
||||
protected $imageService;
|
||||
|
||||
/**
|
||||
* @param OfferRepository $offerRepository
|
||||
* @param FileRepository $fileRepository
|
||||
* @param ImageService $imageService
|
||||
*/
|
||||
public function __construct(
|
||||
OfferRepository $offerRepository,
|
||||
FileRepository $fileRepository,
|
||||
ImageService $imageService
|
||||
) {
|
||||
parent::__construct();
|
||||
|
||||
$this->offerRepository = $offerRepository;
|
||||
$this->fileRepository = $fileRepository;
|
||||
$this->imageService = $imageService;
|
||||
}
|
||||
|
||||
public function initializeListAction()
|
||||
{
|
||||
if (!$this->request->hasArgument('filterSettings')) {
|
||||
$filterSettings = new FilterSettings();
|
||||
$this->request->setArgument('filterSettings', $filterSettings);
|
||||
} else {
|
||||
$propertyMappingConfiguration = $this->arguments['filterSettings']->getPropertyMappingConfiguration();
|
||||
$propertyMappingConfiguration->allowAllProperties();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \EP\EpEvents\Domain\Dto\FilterSettings $filterSettings
|
||||
* @return string
|
||||
*/
|
||||
public function listAction(FilterSettings $filterSettings)
|
||||
{
|
||||
$offers = $this->offerRepository->findByFilterSettings($filterSettings);
|
||||
$filterOptions = FilterOptionsCollector::process($offers);
|
||||
$listData = [];
|
||||
|
||||
foreach ($offers as $uid => $offer) {
|
||||
/** @var Offer $offer */
|
||||
$offerDetailPageUri = $this->getDetailPageUri($offer['detailPageUid']);
|
||||
|
||||
$teaserImageUri = $this->getTeaserImageUri($uid);
|
||||
if (!$teaserImageMobileUri = $this->getTeaserImageUri($uid, 'image_teaser_m', 'mobile')) {
|
||||
$teaserImageMobileUri = $this->getTeaserImageUri($uid, 'image_teaser', 'mobile');
|
||||
}
|
||||
|
||||
$listData[] = [
|
||||
'name' => SoftHyphenService::replace($offer['name']),
|
||||
'cssClass' => SectionCssClassService::getClass($offer['configuratorType']),
|
||||
'detailPageUri' => $offerDetailPageUri,
|
||||
'locationName' => $offer['locationName'],
|
||||
'teaserImage' => $teaserImageUri,
|
||||
'teaserImageMobile' => $teaserImageMobileUri,
|
||||
];
|
||||
}
|
||||
|
||||
return json_encode([
|
||||
'offers' => $listData,
|
||||
'filterSettings' => $filterSettings,
|
||||
'filterOptions' => $filterOptions,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $detailPageUid
|
||||
* @return string
|
||||
*/
|
||||
protected function getDetailPageUri($detailPageUid)
|
||||
{
|
||||
return $this
|
||||
->uriBuilder
|
||||
->reset()
|
||||
->setCreateAbsoluteUri(true)
|
||||
->setTargetPageUid($detailPageUid)
|
||||
->buildFrontendUri();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $offerUid
|
||||
* @param string $field
|
||||
* @param string $cropVariant
|
||||
* @return string
|
||||
*/
|
||||
protected function getTeaserImageUri($offerUid, $field = 'image_teaser', $cropVariant = 'default')
|
||||
{
|
||||
$teaserImages = $this->fileRepository->findByRelation(
|
||||
'tx_epevents_domain_model_offer',
|
||||
$field,
|
||||
$offerUid
|
||||
);
|
||||
|
||||
if (count($teaserImages) === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$teaserImage = reset($teaserImages);
|
||||
|
||||
$instructions = ['width' => '500'];
|
||||
$cropVariantCollection = CropVariantCollection::create((string)$teaserImage->getProperty('crop'));
|
||||
$cropArea = $cropVariantCollection->getCropArea($cropVariant);
|
||||
$instructions['crop'] = $cropArea->isEmpty() ? null : $cropArea->makeAbsoluteBasedOnFile($teaserImage);
|
||||
$processedTeaserImage = $this->imageService->applyProcessingInstructions($teaserImage, $instructions);
|
||||
|
||||
return $this->imageService->getImageUri($processedTeaserImage, true);
|
||||
}
|
||||
}
|
||||
@@ -31,6 +31,7 @@ use EP\EpEvents\Domain\Model\Offer;
|
||||
use EP\EpEvents\Domain\Repository\OfferRepository;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
|
||||
use TYPO3\CMS\Extbase\Utility\LocalizationUtility;
|
||||
|
||||
class OfferController extends ActionController
|
||||
{
|
||||
@@ -65,6 +66,22 @@ class OfferController extends ActionController
|
||||
$this->view->assign('content', $content);
|
||||
}
|
||||
|
||||
public function listAction()
|
||||
{
|
||||
$content = $this->configurationManager->getContentObject()->data;
|
||||
$language = [
|
||||
'type' => LocalizationUtility::translate('tx_epevents.label.configurator_travel_type', 'ep_events'),
|
||||
'destination' => LocalizationUtility::translate('tx_epevents.label.configurator_destination', 'ep_events'),
|
||||
'distance' => LocalizationUtility::translate('tx_epevents.label.configurator_distance', 'ep_events'),
|
||||
'hotel' => LocalizationUtility::translate('tx_epevents.label.configurator_accommodation', 'ep_events'),
|
||||
'programme' => LocalizationUtility::translate('tx_epevents.label.configurator_programme', 'ep_events'),
|
||||
'noSelection' => LocalizationUtility::translate('tx_epevents.label.configurator_please_select', 'ep_events'),
|
||||
'reset' => LocalizationUtility::translate('tx_epevents.label.reset', 'ep_events'),
|
||||
];
|
||||
$this->view->assign('content', $content);
|
||||
$this->view->assign('language', json_encode($language));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Offer $offer
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
<?php
|
||||
namespace EP\EpEvents\Domain\Dto;
|
||||
|
||||
class FilterSettings
|
||||
{
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $travelType;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $locationType;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $locationDistance;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $hotelType;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $activityType;
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getTravelType()
|
||||
{
|
||||
return $this->travelType;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $travelType
|
||||
*/
|
||||
public function setTravelType($travelType)
|
||||
{
|
||||
$this->travelType = $travelType;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getLocationType()
|
||||
{
|
||||
return $this->locationType;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $locationType
|
||||
*/
|
||||
public function setLocationType($locationType)
|
||||
{
|
||||
$this->locationType = $locationType;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getLocationDistance()
|
||||
{
|
||||
return $this->locationDistance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $locationDistance
|
||||
*/
|
||||
public function setLocationDistance($locationDistance)
|
||||
{
|
||||
$this->locationDistance = $locationDistance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getHotelType()
|
||||
{
|
||||
return $this->hotelType;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $hotelType
|
||||
*/
|
||||
public function setHotelType($hotelType)
|
||||
{
|
||||
$this->hotelType = $hotelType;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getActivityType()
|
||||
{
|
||||
return $this->activityType;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $activityType
|
||||
*/
|
||||
public function setActivityType($activityType)
|
||||
{
|
||||
$this->activityType = $activityType;
|
||||
}
|
||||
}
|
||||
@@ -27,8 +27,10 @@ namespace EP\EpEvents\Domain\Repository;
|
||||
* This copyright notice MUST APPEAR in all copies of the script!
|
||||
***************************************************************/
|
||||
|
||||
use EP\EpEvents\Domain\Dto\FilterSettings;
|
||||
use EP\EpEvents\Domain\Model\Activity;
|
||||
use EP\EpEvents\Domain\Model\Offer;
|
||||
use TYPO3\CMS\Core\Database\ConnectionPool;
|
||||
use TYPO3\CMS\Extbase\Persistence\QueryInterface;
|
||||
|
||||
class OfferRepository extends AbstractRepository
|
||||
@@ -74,6 +76,72 @@ class OfferRepository extends AbstractRepository
|
||||
return $query->matching($query->logicalAnd($constraints))->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param FilterSettings $filterSettings
|
||||
* @return array
|
||||
*/
|
||||
public function findByFilterSettings(FilterSettings $filterSettings)
|
||||
{
|
||||
/** @var ConnectionPool $connectionPool */
|
||||
$connectionPool = $this->objectManager->get(ConnectionPool::class);
|
||||
$qb = $connectionPool->getQueryBuilderForTable('tx_epevents_domain_model_offer');
|
||||
|
||||
$qb->select(
|
||||
'o.uid AS uid', 'o.name AS name', 'o.configurator_type AS configuratorType',
|
||||
'o.detail_page AS detailPageUid',
|
||||
'l.name AS locationName', 'l.configurator_type AS locationType',
|
||||
'l.configurator_distance AS locationDistance', 'h.configurator_type AS hotelType',
|
||||
'a.configurator_type AS activityType'
|
||||
)
|
||||
->from('tx_epevents_domain_model_offer', 'o')
|
||||
->join('o', 'tx_epevents_domain_model_location', 'l', 'o.location = l.uid')
|
||||
->join('o', 'tx_epevents_domain_model_hotel', 'h', 'o.hotel = h.uid')
|
||||
->join('o', 'tx_epevents_offer_activity_mm', 'mm', 'mm.uid_local = o.uid')
|
||||
->join('mm', 'tx_epevents_domain_model_activity', 'a', 'mm.uid_foreign = a.uid')
|
||||
->where($qb->expr()->neq('o.type', Offer::TYPE_CLIENT))
|
||||
->orderBy('l.name', 'ASC')
|
||||
->addOrderBy('o.name', 'ASC');
|
||||
|
||||
if ($filterSettings->getTravelType() > 0) {
|
||||
$qb->andWhere($qb->expr()->eq(
|
||||
'o.configurator_type', $qb->createNamedParameter($filterSettings->getTravelType())
|
||||
));
|
||||
}
|
||||
if ($filterSettings->getLocationDistance() > 0) {
|
||||
$qb->andWhere($qb->expr()->eq('l.configurator_distance', $filterSettings->getLocationDistance()));
|
||||
}
|
||||
if ($filterSettings->getLocationType() > 0) {
|
||||
$qb->andWhere($qb->expr()->eq('l.configurator_type', $filterSettings->getLocationType()));
|
||||
}
|
||||
if ($filterSettings->getHotelType() > 0) {
|
||||
$qb->andWhere($qb->expr()->eq('h.configurator_type', $filterSettings->getHotelType()));
|
||||
}
|
||||
if ($filterSettings->getActivityType() > 0) {
|
||||
$qb->andWhere($qb->expr()->eq('a.configurator_type', $filterSettings->getActivityType()));
|
||||
}
|
||||
|
||||
$result = $qb->execute()->fetchAll();
|
||||
$offers = [];
|
||||
|
||||
foreach ($result as $row) {
|
||||
if (!array_key_exists($row['uid'], $offers)) {
|
||||
$offers[$row['uid']] = [
|
||||
'name' => $row['name'],
|
||||
'configuratorType' => $row['configuratorType'],
|
||||
'detailPageUid' => $row['detailPageUid'],
|
||||
'locationType' => $row['locationType'],
|
||||
'locationName' => $row['locationName'],
|
||||
'locationDistance' => $row['locationDistance'],
|
||||
'hotelType' => $row['hotelType'],
|
||||
'activityTypes' => [],
|
||||
];
|
||||
}
|
||||
$offers[$row['uid']]['activityTypes'][] = $row['activityType'];
|
||||
}
|
||||
|
||||
return $offers;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Activity $activity
|
||||
* @return array|\TYPO3\CMS\Extbase\Persistence\QueryResultInterface
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
namespace EP\EpEvents\Service;
|
||||
|
||||
use EP\EpEvents\Domain\Model\Offer;
|
||||
use TYPO3\CMS\Extbase\Utility\LocalizationUtility;
|
||||
|
||||
class FilterOptionsCollector
|
||||
{
|
||||
/**
|
||||
* @param array $offers
|
||||
* @return array
|
||||
*/
|
||||
public static function process(array $offers)
|
||||
{
|
||||
$settings = [
|
||||
'travelTypes' => [],
|
||||
'locationTypes' => [],
|
||||
'locationDistances' => [],
|
||||
'hotelTypes' => [],
|
||||
'activityTypes' => [],
|
||||
];
|
||||
|
||||
foreach ($offers as $offer) {
|
||||
/** @var Offer $offer */
|
||||
if (!array_key_exists($offer['configuratorType'], $settings['travelTypes'])) {
|
||||
$settings['travelTypes'][$offer['configuratorType']] = [
|
||||
'type' => $offer['configuratorType'],
|
||||
'label' => LocalizationUtility::translate(sprintf('tx_epevents_domain_model_traveltype.configurator_label.%d', $offer['configuratorType']), 'ep_events'),
|
||||
];
|
||||
}
|
||||
if (!array_key_exists($offer['locationType'], $settings['locationTypes'])) {
|
||||
$settings['locationTypes'][$offer['locationType']] = [
|
||||
'type' => $offer['locationType'],
|
||||
'label' => LocalizationUtility::translate(sprintf('tx_epevents_domain_model_location.configurator_label.%d', $offer['locationType']), 'ep_events'),
|
||||
];
|
||||
}
|
||||
if (!array_key_exists($offer['locationDistance'], $settings['locationDistances'])) {
|
||||
$settings['locationDistances'][$offer['locationDistance']] = [
|
||||
'type' => $offer['locationDistance'],
|
||||
'label' => LocalizationUtility::translate(sprintf('tx_epevents_domain_model_distance.configurator_label.%d', $offer['locationDistance']), 'ep_events'),
|
||||
];
|
||||
}
|
||||
if (!array_key_exists($offer['hotelType'], $settings['hotelTypes'])) {
|
||||
$settings['hotelTypes'][$offer['hotelType']] = [
|
||||
'type' => $offer['hotelType'],
|
||||
'label' => LocalizationUtility::translate(sprintf('tx_epevents_domain_model_hotel.configurator_label.%d', $offer['hotelType']), 'ep_events'),
|
||||
];
|
||||
}
|
||||
foreach ($offer['activityTypes'] as $activityType) {
|
||||
if (!array_key_exists($activityType, $settings['activityTypes'])) {
|
||||
$settings['activityTypes'][$activityType] = [
|
||||
'type' => $activityType,
|
||||
'label' => LocalizationUtility::translate(sprintf('tx_epevents_domain_model_activity.configurator_label.%d', $activityType), 'ep_events'),
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sort($settings['travelTypes']);
|
||||
sort($settings['locationTypes']);
|
||||
sort($settings['locationDistances']);
|
||||
sort($settings['hotelTypes']);
|
||||
sort($settings['activityTypes']);
|
||||
|
||||
return $settings;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
namespace EP\EpEvents\Service;
|
||||
|
||||
class SectionCssClassService
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected static $cssClasses = [
|
||||
1 => 'incentives',
|
||||
2 => 'teambuilding',
|
||||
3 => 'meetings',
|
||||
4 => 'events',
|
||||
];
|
||||
|
||||
/**
|
||||
* @param int $sectionValue
|
||||
* @return string
|
||||
*/
|
||||
public static function getClass($sectionValue = null)
|
||||
{
|
||||
if (array_key_exists($sectionValue, static::$cssClasses)) {
|
||||
return 'section-' . static::$cssClasses[$sectionValue];
|
||||
}
|
||||
|
||||
return 'section-default';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
namespace EP\EpEvents\Service;
|
||||
|
||||
class SoftHyphenService
|
||||
{
|
||||
/**
|
||||
* @param $string
|
||||
* @return string
|
||||
*/
|
||||
public static function replace($string)
|
||||
{
|
||||
return str_replace('--', '­', $string);
|
||||
}
|
||||
}
|
||||
@@ -27,6 +27,7 @@ namespace EP\EpEvents\ViewHelpers;
|
||||
* This copyright notice MUST APPEAR in all copies of the script!
|
||||
***************************************************************/
|
||||
|
||||
use EP\EpEvents\Service\SectionCssClassService;
|
||||
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
|
||||
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
|
||||
use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic;
|
||||
@@ -35,16 +36,6 @@ class SectionCssClassViewHelper extends AbstractViewHelper
|
||||
{
|
||||
use CompileWithRenderStatic;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected static $cssClasses = [
|
||||
1 => 'incentives',
|
||||
2 => 'teambuilding',
|
||||
3 => 'meetings',
|
||||
4 => 'events',
|
||||
];
|
||||
|
||||
public function initializeArguments()
|
||||
{
|
||||
parent::initializeArguments();
|
||||
@@ -68,11 +59,8 @@ class SectionCssClassViewHelper extends AbstractViewHelper
|
||||
if ($sectionValue === null) {
|
||||
$sectionValue = $arguments['sectionValue'];
|
||||
}
|
||||
if (array_key_exists($sectionValue, static::$cssClasses)) {
|
||||
return 'section-' . static::$cssClasses[$sectionValue];
|
||||
}
|
||||
|
||||
return 'section-default';
|
||||
return SectionCssClassService::getClass($sectionValue);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ namespace EP\EpEvents\ViewHelpers;
|
||||
* This copyright notice MUST APPEAR in all copies of the script!
|
||||
***************************************************************/
|
||||
|
||||
use EP\EpEvents\Service\SoftHyphenService;
|
||||
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
|
||||
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
|
||||
use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic;
|
||||
@@ -64,7 +65,7 @@ class SoftHyphenViewHelper extends AbstractViewHelper
|
||||
$string = $arguments['string'];
|
||||
}
|
||||
|
||||
return str_replace('--', '­', $string);
|
||||
return SoftHyphenService::replace($string);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user