src/Elements/Bundle/DemiFrontendBundle/Controller/ListController.php line 90

Open in your IDE?
  1. <?php
  2. /**
  3.  * Elements DeMI
  4.  *
  5.  * This source file is available under the elements DeMI license version 1
  6.  *
  7.  * @copyright  Copyright (c) elements.at New Media Solutions GmbH (https://www.elements.at/)
  8.  * @license    elements DeMI Lizenz Version 1 (https://www.elements.at/de/demi-lizenz)
  9.  */
  10. namespace Elements\Bundle\DemiFrontendBundle\Controller;
  11. use Carbon\Carbon;
  12. use Elements\Bundle\CmsToolsBundle\Tool\Helper\ElementsCustomDateFormat;
  13. use Elements\Bundle\DemiFrontendBundle\Service\AccommodationResultSet;
  14. use Elements\Bundle\DemiFrontendBundle\Service\BubbleCalculator;
  15. use Elements\Bundle\DemiFrontendBundle\Service\BubbleCalculatorCorridor;
  16. use Elements\Bundle\DemiFrontendBundle\Service\BubbleCalculatorHousePackages;
  17. use Elements\Demi\Frontend\Service\Configuration;
  18. use Elements\Bundle\DemiFrontendBundle\Service\DemiStateHelper;
  19. use Elements\Bundle\DemiFrontendBundle\Service\DemiUrl;
  20. use Elements\Bundle\DemiFrontendBundle\Service\PaginatorHelper;
  21. use Elements\Bundle\DemiFrontendBundle\Service\SearchFrontend;
  22. use Elements\Bundle\DemiFrontendBundle\Service\SortFactory;
  23. use Elements\Bundle\DemiFrontendBundle\Service\SortFactoryNearbySearch;
  24. use Elements\Bundle\SeoHelperBundle\Templating\Helper\Robots;
  25. use Elements\Demi\Accommodation\Search\Listing;
  26. use Elements\Demi\Accommodation\Search\Listing\VacancyLocal;
  27. use Elements\Demi\Accommodation\Search\Parameter;
  28. use Elements\Demi\Accommodation\Search\ResultSet;
  29. use Elements\Demi\Deskline\Accommodation\Search\Service\Vacancies\Live;
  30. use Elements\Demi\Model\AccommodationServiceProvider;
  31. use Elements\Demi\Model\Category;
  32. use Elements\Demi\Model\Classification;
  33. use Elements\Demi\Model\Facility;
  34. use Elements\Demi\Model\HolidayTheme;
  35. use Elements\Demi\Model\HousePackageMaster;
  36. use Elements\Demi\Model\MarketingGroup;
  37. use Elements\Demi\Model\Region;
  38. use Elements\Demi\Model\Stars;
  39. use Elements\Demi\Model\Town;
  40. use ErrorException;
  41. use Exception;
  42. use Jaybizzle\CrawlerDetect\CrawlerDetect;
  43. use Pimcore\Bundle\EcommerceFrameworkBundle\Factory;
  44. use Pimcore\Model\DataObject\DemiFilterObject;
  45. use Pimcore\Model\DataObject\DemiStars;
  46. use Pimcore\Model\Document;
  47. use Pimcore\Model\Document\Snippet;
  48. use Pimcore\Tool\DeviceDetector;
  49. use Pimcore\Translation\Translator;
  50. use Pimcore\Twig\Extension\Templating\PimcoreUrl;
  51. use Symfony\Component\DependencyInjection\ContainerInterface;
  52. use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
  53. use Symfony\Component\HttpFoundation\JsonResponse;
  54. use Symfony\Component\HttpFoundation\Request;
  55. use Symfony\Component\HttpFoundation\Response;
  56. use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
  57. class ListController extends AbstractDemiController
  58. {
  59.     protected array $enabledFeatures = [];
  60.     public function __construct(Configuration                 $demiConfigurationHelper,
  61.                                 protected SearchFrontend $searchParams,
  62.                                 protected VacancyLocal $vacancyLocal,
  63.                                 protected BubbleCalculator              $bubbleCalculator,
  64.                                 protected BubbleCalculatorCorridor $bubbleCalculatorCorridor,
  65.                                 protected BubbleCalculatorHousePackages $bubbleCalculatorHousePackages,
  66.                                 protected Robots $robots,
  67.                                 protected VacancyLocal $corridor,
  68.                                 protected ?SortFactoryNearbySearch      $sortFactoryNearbySearch,
  69.                                 protected SortFactory $sortFactoryHelper,
  70.                                 protected ?Live                         $desklineLive,
  71.                                 protected DemiUrl $demiUrl,
  72.                                 protected Translator $translator,
  73.                                 protected AccommodationResultSet $demiAccommodationResultSetHelper,
  74.                                 protected DemiStateHelper               $demiStateHelper,
  75.                                 protected PimcoreUrl $pimcoreUrl,
  76.                                 protected PaginatorHelper $paginatorHelper,
  77.                                 protected ParameterBagInterface $parameterBag,
  78.                                 ElementsCustomDateFormat $elementsCustomDateFormat
  79.     ) {
  80.         $this->setConfiguration($demiConfigurationHelper);
  81.         $this->setElementsCustomDateFormat($elementsCustomDateFormat);
  82.         $this->enabledFeatures $this->parameterBag->has("elements_demi.enabled_features") ? $this->parameterBag->get("elements_demi.enabled_features") : [];
  83.     }
  84.     public function listAction(Request $request): JsonResponse|Response
  85.     {
  86.         $crawlerDetect = new CrawlerDetect();
  87.         if($crawlerDetect->isCrawler()){
  88.             $params $request->query->all();
  89.             foreach(array_keys($params) as $paramKey){
  90.                 if(!in_array($paramKey,['randSeed''page''asSitemapXml'])){
  91.                     //TODO logrotate / cleanup?
  92.                     file_put_contents(DEMI_LOG_PATH '/blocked_crawler_request.log'"[" Carbon::now()->format("Y-m-d H:i:s") . "] " .  $crawlerDetect->getMatches(). ": " $request->getRequestUri() .PHP_EOLFILE_APPEND LOCK_EX);
  93.                     //bye, bye crawler
  94.                     $this->redirect($this->configuration->getAccoParentDocument(), 303);
  95.                 }
  96.             }
  97.         }
  98.         $viewParams["accommodationListTilesEnabled"] = $this->enabledFeatures['accommodationListTiles'];
  99.         $deviceDetector DeviceDetector::getInstance();
  100.         $device $deviceDetector->isPhone() ? 'mobile' 'desktop';
  101.         if (!$this->editmode) {
  102.             if (empty($this->configuration->getCurrentDocument())) {
  103.                 $this->configuration->setDocument($this->document ?? $request->attributes->get('contentDocument'));
  104.             }
  105.             $isNoDate $request->get('from') === null && $request->get('to') === null;
  106.             $viewParams["noDate"] = $isNoDate;
  107.             if ($redirect $this->needsTimeStringRedirect($request)) {
  108.                 return $this->redirect($redirect);
  109.             }
  110.             $searchError null;
  111.             $useAlternativeSearch false;
  112.             $searchParams $this->searchParams->getSearchParam(nullfalsetrue);
  113.             if (!$isNoDate && empty($searchParams->getRoomrows())) {
  114.                 //faulty search parameters
  115.                 $searchError true;
  116.             }
  117.             $housePackageContainer $this->document->getProperty('demi_housePackageContainer');
  118.             $housePackageMasterSelfAssign $this->document->getProperty('demi_housePackageMasterSelfAssign');
  119.             if (!$this->editmode && $housePackageContainer !== null) {
  120.                 if (!$housePackageContainer->isPublished()) {
  121.                     throw new NotFoundHttpException('List uses unpublished HousePackageContainer');
  122.                 }
  123.                 if (!empty($housePackageContainer->getPackageDuration())) {
  124.                     $viewParams["tvbPackage"] = $housePackageContainer;
  125.                 }
  126.             } elseif (!$this->editmode && $housePackageMasterSelfAssign !== null) {
  127.                 if (!$housePackageMasterSelfAssign->isPublished()) {
  128.                     throw new NotFoundHttpException('List uses unpublished HousePackageMasterSelfAssign');
  129.                 }
  130.             }
  131.             $itemCountPerPage 10;
  132.             if (!$deviceDetector->isPhone()) {
  133.                 $itemCountPerPage $this->configuration->getItemCountPerPageDesktop();
  134.             }
  135.             // for list-search-params we set the currentItemCountPerPage and page since this needs to be passed in case of a life request
  136.             $searchParams->setCurrentItemCountPerPage($itemCountPerPage);
  137.             $searchParams->setPage($request->get('page'1));
  138.             $accoParentFullPath $this->configuration->getAccoParentDocument()->getFullPath();
  139.             if (str_starts_with($accoParentFullPath'http://') || str_starts_with($accoParentFullPath'https://')) {
  140.                 //acco parent document seems to be from a different site
  141.                 if (!str_starts_with($accoParentFullPath$request->getSchemeAndHttpHost())) {
  142.                     //just to make sure that domains really differ
  143.                     throw new ErrorException('Please check config Object in use, it does not match the current domain. Acco parent document location: ' $this->configuration->getAccoParentDocument()->getFullPath() . ' vs. current request scheme and host: ' $request->getSchemeAndHttpHost());
  144.                 }
  145.             }
  146.             if ($searchParams->getIsCorridor()) {
  147.                 $listing $this->corridor;
  148.             } else {
  149.                 $listing $this->vacancyLocal;
  150.             }
  151.             $listing->setSortFactories(array_merge($this->sortFactoryHelper->getAdditionalSortFactories(),
  152.                 [$this->sortFactoryNearbySearch]));
  153.             if ($searchParams->getSearchType() == Parameter::SEARCH_TYPE_ONE_PRODUCT_PER_PACKAGE) {
  154.                 $listing->setReturnType(Listing::RETURNTYPE_PACKAGES);
  155.                 $viewParams["isPackageSearch"] = true;
  156.             } else {
  157.                 $viewParams["isPackageSearch"] = false;
  158.             }
  159.             $viewParams["language"] = $request->getLocale();
  160.             $searchParams->setSkipCleaningSortAfterFactory((bool)$request->get('skipCleaningSortAfterFactory'));
  161.             $listing->setParameter($searchParams);
  162.             if ($request->get('isAlternative')) {
  163.                 $itemCountPerPage -= $searchParams->getPreviousResults();
  164.             }
  165.             $paginator $this->paginatorHelper->getPaginator($listing$itemCountPerPage$request->get('page'1)); // new Paginator($listing);
  166.             $viewParams["paginator"] = $paginator;
  167.             if ($searchError || $paginator->getTotalItemCount() == 0) {
  168.                 $error $this->searchParams->getSearchError($searchParams);
  169.                 $searchError $error;
  170.             }
  171.             $isEnabledAlternativeSearch $this->configuration->getEnableAlternativeSearch();
  172.             $viewParams["enabledAlternativeSearch"] = $isEnabledAlternativeSearch;
  173.             $minResult $this->configuration->getMinAccoCntFiltering();
  174.             $viewParams["minResult"] = $minResult;
  175.             if ($isEnabledAlternativeSearch && $searchParams->getDateFrom() && $searchParams->getDateTo() &&
  176.                 (!$searchParams->getNights() || ($searchParams->getNights() == '')) && ($searchError == null)) {
  177.                 $useAlternativeSearch true;
  178.             }
  179.             if ($request->get('isAlternative')) {
  180.                 if ($paginator->getTotalItemCount() == 0) {
  181.                     $viewParams["showAlternativeResults"] = false;
  182.                 } else {
  183.                     $viewParams["showAlternativeResults"] = true;
  184.                     $request->query->remove('ajax');
  185.                     $request->query->remove('excludeAccoIds');
  186.                     $request->query->remove('isAlternative');
  187.                     $request->query->remove('previousResults');
  188.                     $queryString '?' $this->getQueryStringFromParams($request->query->all());
  189.                     $viewParams["reloadListUrl"] = $queryString;
  190.                 }
  191.             } elseif ($useAlternativeSearch && $paginator->getTotalItemCount() <= $minResult) {
  192.                 $viewParams["makeAlternativeSearchRequest"] = true;
  193.                 $excludeAccoIds $listing->getResultSet()->getAccoIds();
  194.                 // TODO use excludeProductIds for package list
  195.                 $alterantiveUrl $this->getAlternativeSearchUrl($searchParams$request$excludeAccoIds);
  196.                 $viewParams["accoListAlternativeUrl"] = $alterantiveUrl;
  197.             } else {
  198.                 $viewParams["makeAlternativeSearchRequest"] = false;
  199.             }
  200.             $viewParams["availabilityIsChecked"] = !$searchParams->isNoDate();
  201.             $viewParams["searchParam"] = $searchParams;
  202.             $viewParams["error"] = $request->get('error');
  203.             if ($searchError) {
  204.                 $viewParams["searchError"] = $searchError;
  205.             }
  206.             $priceRange $listing->getPriceRange();
  207.             // if minPrice and maxPrice equal - set min to 0
  208.             if ($priceRange[0] == $priceRange[1]) {
  209.                 $priceRange[0] = 0;
  210.             }
  211.             if ($this->configuration->getContentScoreFilterAccommodation() > 0) {
  212.                 $searchParams->setContentScore($this->configuration->getContentScoreFilterAccommodation());
  213.             }
  214.             $viewParams["minPrice"] = $priceRange[0];
  215.             $viewParams["maxPrice"] = $priceRange[1];
  216.             $viewParams["freeCancellationInList"] = $this->enabledFeatures['freeCancellationInList'];
  217.             if ($request->isXmlHttpRequest()) {
  218.                 $this->addResponseHeader('X-Robots-Tag''noindex,nofollow');
  219.             } else {
  220.                 $this->setRobots($request);
  221.             }
  222.             if ($request->get('asSitemapXml')) {
  223.                 if (!$searchParams->isNoDate()) {
  224.                     throw new Exception('sitemap cannot have date params!');
  225.                 }
  226.                 $this->addResponseHeader('Content-Type''text/xml');
  227.                 $sitemapdata $this->generateSitemapData($paginator$searchParams$request->getLocale());
  228.                 return $this->renderTemplate('@ElementsDemiFrontend/List/sitemap.html.twig', ['sitemapdata' => $sitemapdata]);
  229.             }
  230.             if ($request->get('asJsonSumary') && $request->isXmlHttpRequest()) {
  231.                 /**
  232.                  * @var ResultSet $resultSet
  233.                  */
  234.                 $resultSet $listing->getResultSet();
  235.                 $data = [
  236.                     'totalItems' => $resultSet->getCount(),
  237.                     'type' => $listing->getReturnType(),
  238.                     'minPrice' => $resultSet->getMinPriceFromBase()
  239.                 ];
  240.                 return $this->json($data);
  241.             }
  242.             if ($request->get('asJsonList')) {
  243.                 $this->addResponseHeader('Content-Type''application/json');
  244.                 $this->robots->setNoIndex();
  245.                 $this->robots->setRobots();
  246.                 $jsonData $this->generateJsonData($listing$searchParams->isNoDate(), $request$housePackageContainer$viewParams["isPackageSearch"]);
  247.                 return $this->renderTemplate('@ElementsDemiFrontend/List/json.html.twig', ['jsonData' => $jsonData]);
  248.             }
  249.             if ($request->get('asJsonTypeAhead')) {
  250.                 $this->addResponseHeader('Content-Type''application/json');
  251.                 $this->robots->setNoIndex();
  252.                 $this->robots->setRobots();
  253.                 return $this->renderTemplate('@ElementsDemiFrontend/List/jsonTypeahead.html.twig', ['localListing' => $paginator'noDate' => $searchParams->isNoDate(), 'page' => $request->get('page')]);
  254.             }
  255.             if ($request->isXmlHttpRequest()) {
  256.                 if ($searchParams->getIsAlternative()) {
  257.                     return $this->renderTemplate('@ElementsDemiFrontend/List/includes/acco-list-alternative-results.html.twig'array_merge($viewParams, ['accommodationPaginator' => $paginator]));
  258.                 }
  259.                 return $this->renderTemplate('@ElementsDemiFrontend/List/list-content.html.twig'array_merge($viewParams, ['accommodationPaginator' => $paginator]));
  260.             }
  261.         }
  262.         return $this->render('@ElementsDemiFrontend/List/list.' $device '.html.twig'$viewParams);
  263.     }
  264.     public function getAlternativeSearchUrl(Parameter $paramsRequest $request, array $excludeAccoIds): string
  265.     {
  266.         $alternativeParams $this->getAlternativeSearchParams($params);
  267.         $newFrom $alternativeParams->getDateFrom();
  268.         $newTo $alternativeParams->getDateTo();
  269.         $nights $alternativeParams->getNights();
  270.         $startNight $nights[0];
  271.         $endNight $nights[count($nights) - 1];
  272.         $alternativeSearchIgnoreFilterFields explode(', '$this->configuration->getAlternativeSearchIgnoreFilterFields());
  273.         $queryString '?ajax=1&isAlternative=1&';
  274.         $existingParams $request->query->all();
  275.         $newParams = [];
  276.         foreach ($existingParams as $key => $value) {
  277.             $newParams[$key] = $this->cleanRequestParam($value);
  278.         }
  279.         $newParams['durationType'] = 'variable';
  280.         $newParams['durationNights'] = $startNight '-' $endNight;
  281.         $newParams['from'] = $this->elementsCustomDateFormat->dateToString($newFrom);
  282.         $newParams['to'] = $this->elementsCustomDateFormat->dateToString($newTo);
  283.         $newParams['priceFrom'] = '';
  284.         $newParams['priceTo'] = '';
  285.         $newParams['excludeAccoIds'] = $excludeAccoIds;
  286.         $newParams['previousResults'] = count($excludeAccoIds);
  287.         foreach ($alternativeSearchIgnoreFilterFields as $key) {
  288.             if (in_array($key$alternativeSearchIgnoreFilterFields)) {
  289.                 $newParams[$key] = '';
  290.             }
  291.         }
  292.         $queryString .= $this->getQueryStringFromParams($newParams);
  293.         return $queryString;
  294.     }
  295.     private function cleanRequestParam(mixed $value): array|string
  296.     {
  297.         if (!is_array($value)) {
  298.             return htmlentities(strip_tags($value));
  299.         }
  300.         $cleaned = [];
  301.         foreach ($value as $k => $v) {
  302.             $cleaned[$k] = $this->cleanRequestParam($v);
  303.         }
  304.         return $cleaned;
  305.     }
  306.     private function getQueryStringFromParams(array $params) : string
  307.     {
  308.         $queryString '';
  309.         foreach ($params as $key => $val) {
  310.             if (!empty($val)) {
  311.                 if (is_string($val) || is_int($val)) {
  312.                     $queryString .= $key '=' $val '&';
  313.                 } elseif (is_array($val)) {
  314.                     foreach ($val as $item) {
  315.                         $queryString .= $key '[]=' $item '&';
  316.                     }
  317.                 }
  318.             }
  319.         }
  320.         return substr($queryString0, -1);
  321.     }
  322.     public function getBubblesAction(Request $request): JsonResponse
  323.     {
  324.         if (empty($this->configuration->getCurrentDocument())) {
  325.             $this->configuration->setDocument($this->document ?? $request->attributes->get('contentDocument'));
  326.         }
  327.         if (!$this->document->getEditable('filterSnippet')) {
  328.             throw new NotFoundHttpException('could calculate bubbles, because could not find filter snippet');
  329.         }
  330.         $filterSnippet $this->document->getEditable('filterSnippet')->getData();
  331.         $enabledFilters $this->configuration->getFilterBlockItems();
  332.         $noBubbles = ['showAcconame''autoComplete''bestPriceType'];
  333.         $requiredBubbles $requiredParams $allArray = [];
  334.         $bestPriceType null;
  335.         if(is_array($filterSnippet) && $filterSnippet['id']){
  336.             $filterSnippet Document::getById($filterSnippet['id']);
  337.         }
  338.         $elements = [];
  339.         if ($filterSnippet instanceof Snippet) {
  340.             $elements $filterSnippet->getEditables();
  341.             if ($filterSnippet->getContentMasterDocument() instanceof Snippet) {
  342.                 $elements array_replace($filterSnippet->getContentMasterDocument()->getEditables(), $elements);
  343.             }
  344.         }
  345.         $allArrayGroupedByElement = [];
  346.         foreach ($elements as $element) {
  347.             $name null;
  348.             foreach ($enabledFilters as $key => $value) {
  349.                 if (strrpos($element->getName(), $key) !== false) {
  350.                     $name $key;
  351.                 }
  352.             }
  353.             if ($name === 'bestPriceType') {
  354.                 $bestPriceType $element->getValue();
  355.             }
  356.             if ($name && array_key_exists($name$enabledFilters) && $enabledFilters[$name]['enabled'] && !in_array($name$noBubbles)) {
  357.                 $requiredBubbles[$element->getName()] = $name;
  358.                 $requiredParams[$name][] = $enabledFilters[$name]['value'];
  359.                 if (method_exists($element'getElements')) {
  360.                     foreach ($element->getElements() as $el) {
  361.                         if ($el) {
  362.                             $allArray[$name][] = $el->getO_id();
  363.                             $allArrayGroupedByElement[$element->getName()][$name][] = $el->getO_id();
  364.                         }
  365.                     }
  366.                 }
  367.             }
  368.         }
  369.         $bubbles = [];
  370.         if ($bestPriceType && $requiredParams['showBestpriceOnly'] && $requiredParams['showBestpriceOnly'][0] && strtolower($requiredParams['showBestpriceOnly'][0]) != strtolower($bestPriceType)) {
  371.             $requiredParams['showBestpriceOnly'][0] = $bestPriceType;
  372.         }
  373.         if (is_array($requiredBubbles)) {
  374.             $bubbleCats $this->setupBubbles($requiredBubbles$allArray$allArrayGroupedByElement$requiredParams$request);
  375.             foreach ($bubbleCats as $bubbleCat) {
  376.                 foreach ($bubbleCat as $key => $bubbleCount) {
  377.                     $bubbles[$key] = $bubbleCount;
  378.                 }
  379.             }
  380.         }
  381.         return $this->json($bubbles);
  382.     }
  383.     protected function setupBubbles(array $requiredBubbles, array $allArray, array $allArrayGroupedByElement, array $requiredParamsRequest $request): array
  384.     {
  385.         if ($request->get('noBubbles')) {
  386.             return [];
  387.         }
  388.         $params $this->searchParams->getSearchParam(nullfalsetrue);
  389.         if ($params->getIsCorridor()) {
  390.             $bubbleHelper $this->bubbleCalculatorCorridor;
  391.         } elseif ($params->getSearchType() == Parameter::SEARCH_TYPE_ONE_PRODUCT_PER_PACKAGE) {
  392.             $bubbleHelper $this->bubbleCalculatorHousePackages;
  393.         } else {
  394.             $bubbleHelper $this->bubbleCalculator;
  395.         }
  396.         $bubbleHelper->setSearchParameter($params);
  397.         $bubbles = [];
  398.         foreach ($requiredBubbles as $key => $bubble) {
  399.             $allRequiredParams $requiredParams[$bubble];
  400.             if (in_array('fo'$allRequiredParamstrue)) {
  401.                 // find out if mixed element
  402.                 $allRequiredInElement = [];
  403.                 foreach ($allArrayGroupedByElement[$key][$bubble] as $foId) {
  404.                     $filterObject DemiFilterObject::getById($foId);
  405.                     $allRequiredParams $requiredParams[$bubble];
  406.                     if ($filterObject) {
  407.                         foreach ($filterObject->getFilterElements() as $item) {
  408.                             // allowed types for filterObjects
  409.                             if ($item instanceof Town) {
  410.                                 $allRequiredParams[] = 'towns';
  411.                             } elseif ($item instanceof Region) {
  412.                                 $allRequiredParams[] = 'regions';
  413.                             } elseif ($item instanceof MarketingGroup) {
  414.                                 $allRequiredParams[] = 'marketingGroups';
  415.                             } elseif ($item instanceof Facility) {
  416.                                 $allRequiredParams[] = 'facilities';
  417.                             } elseif ($item instanceof Stars) {
  418.                                 $allRequiredParams[] = 'stars';
  419.                             } elseif ($item instanceof Classification) {
  420.                                 $allRequiredParams[] = 'classifications';
  421.                             } elseif ($item instanceof Category) {
  422.                                 $allRequiredParams[] = 'categories';
  423.                             } elseif ($item instanceof HolidayTheme) {
  424.                                 $allRequiredParams[] = 'holidayThemes';
  425.                             }
  426.                         }
  427.                         // filterObjects are not allowed to have different types of children, except regions mixed with towns or stars mixed with classifications
  428.                         $allRequiredInElement = [...$allRequiredParams, ...$allRequiredInElement];
  429.                     }
  430.                 }
  431.                 //end
  432.                 $allRequiredInElement array_unique($allRequiredInElement);
  433.                 foreach ($allArrayGroupedByElement[$key][$bubble] as $foId) {
  434.                     $filterObject DemiFilterObject::getById($foId);
  435.                     $allRequiredParams $requiredParams[$bubble];
  436.                     if ($filterObject) {
  437.                         $items = [];
  438.                         foreach ($filterObject->getFilterElements() as $item) {
  439.                             $items[] = $item->getId();
  440.                             // allowed types for filterObjects
  441.                             if ($item instanceof Town) {
  442.                                 $allRequiredParams[] = 'towns';
  443.                             } elseif ($item instanceof Region) {
  444.                                 $allRequiredParams[] = 'regions';
  445.                             } elseif ($item instanceof MarketingGroup) {
  446.                                 $allRequiredParams[] = 'marketingGroups';
  447.                             } elseif ($item instanceof Facility) {
  448.                                 $allRequiredParams[] = 'facilities';
  449.                             } elseif ($item instanceof Stars) {
  450.                                 $allRequiredParams[] = 'stars';
  451.                             } elseif ($item instanceof Classification) {
  452.                                 $allRequiredParams[] = 'classifications';
  453.                             } elseif ($item instanceof Category) {
  454.                                 $allRequiredParams[] = 'categories';
  455.                             } elseif ($item instanceof HolidayTheme) {
  456.                                 $allRequiredParams[] = 'holidayThemes';
  457.                             }
  458.                         }
  459.                         // filterObjects are not allowed to have different types of children, except regions mixed with towns or stars mixed with classifications
  460.                         $allRequiredParams array_unique($allRequiredParams);
  461.                         if (count($allRequiredParams) == || ((count($allRequiredParams) == &&
  462.                                     (in_array('towns'$allRequiredParams) && in_array('regions'$allRequiredParams))) ||
  463.                                 (in_array('stars'$allRequiredParams) && in_array('classifications'$allRequiredParams)))) {
  464.                             if ($allRequiredInElement[0] === 'fo' && count($allRequiredInElement) > 2) {
  465.                                 //filter Elements with filter objects containing different types
  466.                                 $allRequiredParams $allRequiredInElement;
  467.                             }
  468.                             $bubbles[$bubble 'BubbleArray' $foId] = $bubbleHelper->getCountForDestIds($items$allRequiredParams,
  469.                                 stripos($bubble'facilities') !== false, (int)($foId));
  470.                         }
  471.                     }
  472.                 }
  473.             } else {
  474.                 if(!array_key_exists($bubble,$allArray)){
  475.                     $allArray[$bubble] = [];
  476.                 }
  477.                 $bubbles[$bubble 'BubbleArray'] = $bubbleHelper->getCountForDestIds($allArray[$bubble], $allRequiredParams,
  478.                     stripos($bubble'facilities') !== false);
  479.             }
  480.         }
  481.         return $bubbles;
  482.     }
  483.     protected function setRobots(Request $request): void
  484.     {
  485.         //set robots in meta tags
  486.         $robotsParams $request->query->all();
  487.         if (count($robotsParams)) {
  488.             $this->robots->setNoIndex();
  489.             $this->robots->setRobots();
  490.         }
  491.     }
  492.     private function generateSitemapData($paginator$searchParamstring $language): array
  493.     {
  494.         $detailUrls = [];
  495.         $isPackageSearch $searchParam->getSearchType() == Parameter::SEARCH_TYPE_ONE_PRODUCT_PER_PACKAGE;
  496.         if ($isPackageSearch) {
  497.             foreach ($paginator as $resultSet) {
  498.                 $accoObject AccommodationServiceProvider::getById($resultSet->getAccommodationId());
  499.                 $housePackageMaster HousePackageMaster::getById($resultSet->getHousePackageMasterId());
  500.                 $host 'https://' $_SERVER['HTTP_HOST'];
  501.                 $detailUrl $this->demiUrl->invoke([
  502.                     'accommodation' => $accoObject,
  503.                     'package' => $housePackageMaster
  504.                 ], 'demi_acco_detail_package_page'truetrue$language);
  505.                 $detailUrls[]['loc'] = $host $detailUrl;
  506.             }
  507.         } else {
  508.             foreach ($paginator as $resultSet) {
  509.                 $accoObject AccommodationServiceProvider::getById($resultSet->getAccommodationId());
  510.                 $host 'https://' $_SERVER['HTTP_HOST'];
  511.                 $detailUrl $this->demiUrl->invoke([
  512.                     'accommodation' => $accoObject
  513.                 ], 'demi_acco_detail_page'truetrue$language);
  514.                 $detailUrls[]['loc'] = $host $detailUrl;
  515.             }
  516.         }
  517.         return $detailUrls;
  518.     }
  519.     private function generateJsonData($localListing$noDateRequest $request$tvbPackage$isPackageSearch): bool|string
  520.     {
  521.         $results = [];
  522.         foreach ($localListing as $entry) {
  523.             if ($entry->getAccommodationId()) {
  524.                 $accommodation AccommodationServiceProvider::getById($entry->getAccommodationId());
  525.                 if ($accommodation) {
  526.                     $classificationsArray = [];
  527.                     foreach ($accommodation->getClassifications() as $classification) {
  528.                         $count $classification->getClassificationGroupAmount();
  529.                         $group $classification->getClassificationGroup();
  530.                         if ($group && $count) {
  531.                             $classifications = [];
  532.                             $classifications['name'] = $group->getName();
  533.                             $classifications['count'] = $count;
  534.                             if ($group->getIconSmall()) {
  535.                                 $thumbnail $group->getIconSmall()->getThumbnail('demi-classification');
  536.                                 if ($thumbnail) {
  537.                                     $classifications['html'] = $thumbnail->getHTML();
  538.                                 }
  539.                             }
  540.                             $classificationsArray[] = $classifications;
  541.                         }
  542.                     }
  543.                     $badges null;
  544.                     foreach ($accommodation->getClassifications() as $classification) {
  545.                         if (!$classification->getClassificationGroup() && $classification->getName()) {
  546.                             $badges[] = $classification->getName();
  547.                         }
  548.                     }
  549.                     $stars $accommodation->getStars();
  550.                     if ($stars instanceof DemiStars) {
  551.                         $starNumbers $stars->getStarsNumbers();
  552.                         if ($stars->getIsSuperior()) {
  553.                             $superior 'S';
  554.                         }
  555.                     }
  556.                     $totalVacancies $entry->getTotalVacanciesAndOffers()->getTotalVacancies();
  557.                     $totalOffers $entry->getTotalVacanciesAndOffers()->getTotalOffers();
  558.                     if ($totalVacancies 6) {
  559.                         $array = (['[AMOUNT_ROOMS]' => $totalVacancies]);
  560.                         $message $this->translator->trans('demi.only-x-rooms-free'$array);
  561.                         $messageSub $this->translator->trans('demi.list.last-chance');
  562.                     } else {
  563.                         $array = (['[AMOUNT_OFFERS]' => $totalOffers]);
  564.                         $message $this->translator->trans('demi.x-rooms-free', ['[AMOUNT_ROOMS]' => $totalVacancies]);
  565.                         $messageSub $this->translator->trans('demi.choose-from-x-offers'$array);
  566.                     }
  567.                     $minPriceMeal $this->demiAccommodationResultSetHelper->getMinPriceProductMeals($entry);
  568.                     $facilities null;
  569.                     $accoTopFacilities $this->demiAccommodationResultSetHelper->getTopFacilitiesForAccommodation($accommodation$this->configuration->getTopFacilities());
  570.                     foreach ($accoTopFacilities as $topFacility) {
  571.                         $facilities[] = $topFacility->getName();
  572.                     }
  573.                     if ($noDate) {
  574.                         $fromDate null;
  575.                     } else {
  576.                         $fromDate = new Carbon();
  577.                         $fromDate->setTimestamp($this->demiStateHelper->getSearchFrom()->getTimestamp());
  578.                     }
  579.                     if ($tvbPackage) {
  580.                         $detailUrl $this->demiUrl->invoke([
  581.                             'accommodation' => $accommodation,
  582.                             'package' => $tvbPackage
  583.                         ], 'demi_acco_detail_tvbpackage_page'$noDate && !$request->get('nearbySearchId'));
  584.                     } else if ($isPackageSearch) {
  585.                         $housePackageMaster HousePackageMaster::getById($accommodation->getHousePackageMasterId());
  586.                         $detailUrl $this->demiUrl->invoke([
  587.                             'accommodation' => $accommodation,
  588.                             'package' => $housePackageMaster
  589.                         ], 'demi_acco_detail_package_page'$noDate && !$request->get('nearbySearchId'));
  590.                     } else {
  591.                         $detailUrl $this->pimcoreUrl->__invoke([
  592.                             'object' => $accommodation
  593.                         ], 'demi_acco_detail_page'$noDate && !$request->get('nearbySearchId'));
  594.                     }
  595.                     $firstImageUrl $this->demiUrl->invoke([
  596.                         'path' => 'demi',
  597.                         'objectId' => $isPackageSearch $housePackageMaster->getId() : $accommodation->getId(),
  598.                         'fallbackId' => $isPackageSearch $accommodation->getId() : '',
  599.                         'thumbnail' => 'demi-chatbot',
  600.                         'dateFrom' => $fromDate $fromDate->getTimestamp() : ''
  601.                     ], 'demi_image');
  602.                     $defaultCurrency Factory::getInstance()->getEnvironment()->getDefaultCurrency();
  603.                     if ($defaultCurrency) {
  604.                         $currency $defaultCurrency->getSymbol();
  605.                     }
  606.                     $stars $accommodation->getStars();
  607.                     if ($stars instanceof DemiStars) {
  608.                         $starNumbers $stars->getStarsNumbers();
  609.                         if ($stars->getIsSuperior()) {
  610.                             $superior 'S';
  611.                         }
  612.                     }
  613.                     if ($accommodation->getRatingSystem() === 'TrustYou' && $accommodation->getRatingCountDirect() > 10) {
  614.                         $rating = [
  615.                             'score' => $accommodation->getRatingAverageDirect(),
  616.                             'text' => $accommodation->getRatingScoreDescription()
  617.                         ];
  618.                     }
  619.                     $data = [
  620.                         'acco_id' => $accommodation->getId(),
  621.                         'acco_detail_url' => $detailUrl,
  622.                         'acco_thumb' => $firstImageUrl,
  623.                         'acco_headline' => $accommodation->getName(),
  624.                         'acco_stars' => $starNumbers,
  625.                         'acco_type' => $accommodation->getCategoryNames(),
  626.                         'acco_town' => $accommodation->getTown() ? $accommodation->getTown()->getName() : '',
  627.                         'acco_message' => $message,
  628.                         'acco_message_sub' => $messageSub,
  629.                         'acco_price_text' => $this->translator->trans('demi.price-from-short'),
  630.                         'acco_price' => ($entry->getMinPrice() ?: $entry->getMinPriceBase()),
  631.                         'acco_currency' => $currency,
  632.                         'acco_meal' => $minPriceMeal,
  633.                         'acco_facilities' => $facilities,
  634.                         'acco_classification' => $classificationsArray,
  635.                         'acco_rating' => $rating,
  636.                         'acco_superior' => $superior,
  637.                         'acco_vacancies' => $totalVacancies,
  638.                         'acco_best_price' => $accommodation->getBestPrice(),
  639.                         'acco_badges' => $badges
  640.                     ];
  641.                     $results[] = $data;
  642.                 }
  643.             }
  644.         }
  645.         return json_encode($resultsJSON_THROW_ON_ERROR);
  646.     }
  647. }