custom/plugins/SEMKNOX/src/Product/SiteSearchProductListingFeaturesSubscriber.php line 161

Open in your IDE?
  1. <?php
  2. declare(strict_types=1);
  3. namespace semknox\search\Product;
  4. use Doctrine\DBAL\Connection;
  5. use Psr\Cache\InvalidArgumentException;
  6. use Shopware\Core\Content\Product\Events\ProductListingCriteriaEvent;
  7. use Shopware\Core\Content\Product\Events\ProductListingResultEvent;
  8. use Shopware\Core\Content\Product\Events\ProductSearchCriteriaEvent;
  9. use Shopware\Core\Content\Product\Events\ProductSearchResultEvent;
  10. use Shopware\Core\Content\Product\SalesChannel\Listing\FilterCollection;
  11. use Shopware\Core\Content\Product\SalesChannel\Listing\ProductListingFeaturesSubscriber;
  12. use Shopware\Core\Content\Product\SalesChannel\Listing\ProductListingResult;
  13. use Shopware\Core\Content\Product\SalesChannel\Sorting\ProductSortingEntity;
  14. use Shopware\Core\Framework\DataAbstractionLayer\EntityRepositoryInterface;
  15. use Shopware\Core\Framework\Event\ShopwareEvent;
  16. use Shopware\Core\System\SalesChannel\SalesChannelContext;
  17. use Shopware\Core\System\SystemConfig\SystemConfigService;
  18. use Shopware\Storefront\Page\GenericPageLoader;
  19. use Symfony\Component\DependencyInjection\ContainerInterface;
  20. use Symfony\Component\HttpFoundation\Request;
  21. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  22. use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
  23. use Shopware\Core\Content\Product\SalesChannel\Exception\ProductSortingNotFoundException;
  24. use Shopware\Core\Content\Product\SalesChannel\Sorting\ProductSortingCollection;
  25. use function GuzzleHttp\Psr7\parse_query;
  26. use Shopware\Core\Framework\DataAbstractionLayer\Search\Sorting\FieldSorting;
  27. use Shopware\Core\Framework\Uuid\Uuid;
  28. use Symfony\Contracts\Translation\TranslatorInterface;
  29. use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
  30. use Shopware\Core\Framework\Struct\ArrayEntity;
  31. use semknox\search\Framework\SemknoxsearchHelper;
  32. use Shopware\Core\Framework\DataAbstractionLayer\EntityCollection;
  33. use Shopware\Core\Content\Property\Aggregate\PropertyGroupOption\PropertyGroupOptionCollection;
  34. use Shopware\Core\Content\Property\Aggregate\PropertyGroupOption\PropertyGroupOptionEntity;
  35. use Shopware\Core\Framework\DataAbstractionLayer\Search\AggregationResult\Metric\StatsResult;
  36. use Shopware\Core\Framework\DataAbstractionLayer\Search\AggregationResult\Metric\EntityResult;
  37. use Shopware\Core\Content\Property\PropertyGroupEntity;
  38. use Shopware\Core\Content\Property\PropertyGroupCollection;
  39. class SiteSearchProductListingFeaturesSubscriber extends ProductListingFeaturesSubscriber
  40. {
  41.     /** @var string default sort for categories */
  42.     public const DEFAULT_SORT 'score';
  43.     public const DEFAULT_SEARCH_SORT 'score';
  44.     private $filterableType = ['COLLECTION''BUCKET''COLOR'];
  45.     /**
  46.      * @var EntityRepositoryInterface
  47.      */
  48.     private $optionRepository;
  49.     /**
  50.      * @var EntityRepositoryInterface
  51.      */
  52.     private $sortingRepository;    
  53.     /**
  54.      * @var Connection
  55.      */
  56.     private $connection;
  57.     /**
  58.      * @var SystemConfigService
  59.      */
  60.     private $systemConfigService;
  61.     /**
  62.      * @var EventDispatcherInterface
  63.      */
  64.     private $dispatcher;
  65.     /**
  66.      * 
  67.      * @var ProductListingFeaturesSubscriber
  68.      */
  69.     private $decorated;
  70.     private $origSearchEvent=null;
  71.     private $origListingEvent=null;
  72.     /**
  73.      * @var TranslatorInterface
  74.      */
  75.     private $translator;
  76.     /**
  77.      *
  78.      * @var SemknoxsearchHelper
  79.      */
  80.     private $semknoxSearchHelper;
  81.     public function __construct(
  82.         Connection $connection,
  83.         EntityRepositoryInterface $optionRepository,
  84.         EntityRepositoryInterface $productSortingRepository,
  85.         SystemConfigService $systemConfigService,
  86.         EventDispatcherInterface $dispatcher,
  87.         ProductListingFeaturesSubscriber $decoServ,
  88.         TranslatorInterface $trans,
  89.         SemknoxsearchHelper $helper  
  90.         ) {
  91.             $this->optionRepository $optionRepository;
  92.             $this->sortingRepository $productSortingRepository;
  93.             $this->connection $connection;
  94.             $this->systemConfigService $systemConfigService;
  95.             $this->dispatcher $dispatcher;
  96.             $this->decorated $decoServ;
  97.             $this->translator $trans;
  98.             $this->semknoxSearchHelper $helper;
  99.             parent::__construct(
  100.                 $connection,
  101.                 $optionRepository,
  102.                 $productSortingRepository,
  103.                 $systemConfigService,
  104.                 $dispatcher
  105.                 );
  106.     }
  107.     public function handleListingRequest(?ProductListingCriteriaEvent $event): void
  108.     {
  109.         if ( ($event===null) || (get_class($event)!=='Shopware\Core\Content\Product\Events\ProductListingCriteriaEvent') ) {
  110.             $this->decorated->handleListingRequest($event);
  111.             return;
  112.         }
  113.         $this->origListingEvent $event;
  114.         $request $event->getRequest();
  115.         $criteria $event->getCriteria();
  116.         $context $event->getSalesChannelContext();
  117.         $salesChannelID='';$controller='';$langID='';
  118.         if ($criteria->hasExtension('semknoxData')) {
  119.             $semkData $criteria->getExtension('semknoxData');
  120.             $salesChannelID=$semkData->get('salesChannelID');
  121.             $langID $semkData->get('languageID');
  122.             $domainID $semkData->get('domainID');
  123.             $controller=$semkData->get('controller');
  124.         }
  125.         if (trim($controller)=='') {
  126.             $this->decorated->handleListingRequest($event);
  127.             return;            
  128.         }
  129.         if ($this->semknoxSearchHelper->useSiteSearchInListing($context$request) === false) {
  130.             $this->decorated->handleListingRequest($event);
  131.             return;
  132.         }
  133.         $internal $request->get('useinternal');
  134.         if ( (!is_null($internal)) && ($internal == 525) ) {
  135.             $this->decorated->handleListingRequest($event);
  136.             return;
  137.         }
  138.         $limit $event->getCriteria()->getLimit();
  139.         $key $request->get('order');
  140.         if (!isset($key)) { $key null; }
  141.         if ($key=='topseller') {
  142.             $criteria->resetSorting();
  143.             $key=null;
  144.         }
  145.         if (is_null($key)) {
  146.             $this->addQueryToRequest($request'&order=score');
  147.             $event = new ProductListingCriteriaEvent($request$criteria$context);
  148.         }
  149.         $criteria $event->getCriteria();
  150.         $request $event->getRequest();
  151.         $context $event->getSalesChannelContext();
  152.         $limit $limit ?? $event->getCriteria()->getLimit();
  153.         if ($this->siteSearch_allowRequest($event)) {
  154.             $event=$this->addSortingFromListingRequest($event1);
  155.             $event=$this->addFilterFromRequest($event);
  156.             $this->decorated->handleListingRequest($event);
  157.         } else {
  158.             $this->decorated->handleListingRequest($event);
  159.         }
  160.     }
  161.     public function handleSearchRequest(?ProductSearchCriteriaEvent $event): void
  162.     {
  163.         if ( ($event===null) || (get_class($event)!=='Shopware\Core\Content\Product\Events\ProductSearchCriteriaEvent') ) {
  164.             $this->decorated->handleSearchRequest($event);
  165.             return;
  166.         }
  167.         $this->origSearchEvent $event;
  168.         $request $event->getRequest();
  169.         $criteria $event->getCriteria();
  170.         $context $event->getSalesChannelContext();
  171.         if ($this->semknoxSearchHelper->useSiteSearch($context$request) === false) {
  172.             $this->decorated->handleSearchRequest($event);
  173.             return;
  174.         }
  175.         $internal $request->get('useinternal');
  176.         if ( (!is_null($internal)) && ($internal == 525) ) {
  177.             $this->decorated->handleSearchRequest($event);
  178.             return;
  179.         }
  180.         $limit $event->getCriteria()->getLimit();
  181.         $key $request->get('order');
  182.         if (!isset($key)) { $key null; }
  183.         if ($key=='topseller') {
  184.             $criteria->resetSorting();
  185.             $key=null;
  186.         }
  187.         if (is_null($key)) {
  188.             $this->addQueryToRequest($request'&order=score');
  189.             $event = new ProductSearchCriteriaEvent($request$criteria$context);
  190.         }
  191.         $criteria $event->getCriteria();
  192.         $request $event->getRequest();
  193.         $context $event->getSalesChannelContext();
  194.         $limit $limit ?? $event->getCriteria()->getLimit();
  195.         if ($this->siteSearch_allowRequest($event)) {
  196.             $event=$this->addSortingFromRequest($event);
  197.             $event=$this->addFilterFromRequest($event);
  198.             $this->decorated->handleSearchRequest($event);
  199.         } else {
  200.             $this->decorated->handleSearchRequest($event);
  201.         }
  202.     }
  203.     private function getRedirect($result) {
  204.         $ret='';
  205.         $ext=$result->getExtension('semknoxResultData');
  206.         if ($ext) {
  207.             $semknoxData $ext->getVars();
  208.             if (isset($semknoxData['data']['redirect'])) {
  209.                 $ret $semknoxData['data']['redirect'];
  210.             }
  211.         }
  212.         return $ret;
  213.     }
  214.     private function getUseShopwareSearch($result) {
  215.         $ret=0;
  216.         $ext=$result->getExtension('semknoxResultData');
  217.         if ($ext) {
  218.             $semknoxData $ext->getVars();
  219.             if (isset($semknoxData['data']['useInternalSearch'])) {
  220.                 $ret $semknoxData['data']['useInternalSearch'];
  221.             }
  222.         }
  223.         return $ret;
  224.     }
  225.     private function sitesearchHandleSearchResult(?ProductListingResultEvent $event): void{
  226.         $ext=$event->getResult()->getExtension('semknoxResultData');
  227.         if ($ext) {
  228.             $semknoxData $ext->getVars();
  229.             if ( (!isset($semknoxData['data'])) || (!is_array($semknoxData['data'])) || (!isset($semknoxData['data']['metaData'])) ||  (!is_array($semknoxData['data']['metaData'])) ) {
  230.                 if ($this->origSearchEvent) {
  231.                     $this->decorated->handleSearchRequest($this->origSearchEvent);
  232.                 }
  233.                 $this->decorated->handleResult($event);
  234.                 return;
  235.             }
  236.         } else {
  237.             if ($this->origSearchEvent) {
  238.                 $this->decorated->handleSearchRequest($this->origSearchEvent);
  239.             }
  240.             $this->decorated->handleResult($event);
  241.             return;
  242.         }
  243.         $result $event->getResult();
  244.         $redir $this->getRedirect($result);
  245.         $useInternal $this->getUseShopwareSearch($result);
  246.         if ( ($redir!='') || ($useInternal 0) ) {
  247.             return;
  248.         }
  249.         $sortings $result->getCriteria()->getExtension('sortings');
  250.         $event $this->addSortingFromSiteSearch($event);
  251.         parent::handleResult($event);
  252.     }
  253.     private function sitesearchHandleListingResult(?ProductListingResultEvent $event): void{
  254.         $ext=$event->getResult()->getExtension('semknoxResultData');
  255.         if ($ext) {
  256.             $semknoxData $ext->getVars();
  257.             if ( (!isset($semknoxData['data'])) || (!is_array($semknoxData['data'])) || (!isset($semknoxData['data']['metaData'])) ||  (!is_array($semknoxData['data']['metaData'])) ) {
  258.                 if ($this->origSearchEvent) {
  259.                     $this->decorated->handleSearchRequest($this->origSearchEvent);
  260.                 }
  261.                 $this->decorated->handleResult($event);
  262.                 return;
  263.             }
  264.         } else {
  265.             if ($this->origSearchEvent) {
  266.                 $this->decorated->handleSearchRequest($this->origSearchEvent);
  267.             }
  268.             $this->decorated->handleResult($event);
  269.             return;
  270.         }
  271.         $result $event->getResult();
  272.         $redir $this->getRedirect($result);
  273.         if ( ($redir!='') ) {
  274.             return;
  275.         }
  276.         $this->addCurrentListingFilters($event);
  277.         $result->setPage($this->getPage($event->getRequest()));
  278.         $result->setLimit($this->getLimit($event->getRequest(), $event->getSalesChannelContext()));
  279.         $event $this->addSortingFromSiteSearch($event);
  280.     }
  281.     public function handleResult(?ProductListingResultEvent $event): void {
  282.         if ( ($event!==null) && (get_class($event)==='Shopware\Core\Content\Product\Events\ProductSearchResultEvent') ) {
  283.             $this->sitesearchHandleSearchResult($event);
  284.             return;
  285.         }
  286.         if ( ($event!==null) && (get_class($event)==='Shopware\Core\Content\Product\Events\ProductListingResultEvent') ) {
  287.             $this->sitesearchHandleListingResult($event);
  288.             return;
  289.         }
  290.         $this->decorated->handleResult($event);
  291.     }
  292.     /**
  293.      * check whether the request should be handled by SiteSearch360
  294.      *
  295.      * @throws InvalidArgumentException
  296.      */
  297.     private function siteSearch_allowRequest(ProductListingCriteriaEvent $event): bool
  298.     {
  299.         return true;
  300.     }
  301.     private function deprecated___sitesearch_addResultSorting(ProductListingResultEvent $event): void
  302.     {
  303.         $defaultSort $event instanceof ProductSearchResultEvent self::DEFAULT_SEARCH_SORT self::DEFAULT_SORT;
  304.         $currentSorting $this->siteSearch_getCurrentSorting($event->getRequest(), $defaultSort);
  305.         $event->getResult()->setSorting($currentSorting);
  306.         $this->sortingRegistry->add(
  307.             new ProductSortingEntity('score''filter.sortByScore', ['_score' => 'desc'])
  308.             );
  309.         $sortings $this->sortingRegistry->getSortings();
  310.         /** @var ProductSortingEntity $sorting */
  311.         foreach ($sortings as $sorting) {
  312.             $sorting->setActive($sorting->getKey() === $currentSorting);
  313.         }
  314.         $event->getResult()->setSortings($sortings);
  315.     }
  316.     private function deprecated___siteSearch_getCurrentSorting(Request $requeststring $default): ?string
  317.     {
  318.         $key $request->get('order'$default);
  319.         /*
  320.         if (versionLowerThan('6.2')) {
  321.             $key = $request->get('sort', $default);
  322.         }
  323.         */
  324.         if (!$key) {
  325.             return null;
  326.         }
  327.         if ($this->sortingRegistry->has($key)) {
  328.             return $key;
  329.         }
  330.         return $default;
  331.     }
  332.     public function removeScoreSorting(ProductListingResultEvent $event): void
  333.     {
  334.         $ext=$event->getResult()->getExtension('semknoxResultData');
  335.         if ($ext) {
  336.             $semknoxData $ext->getVars();
  337.             if ( (isset($semknoxData['data'])) && (is_array($semknoxData['data'])) ) {
  338.                 return;
  339.             }
  340.         }
  341.         $sortings $event->getResult()->getAvailableSortings();
  342.         $defaultSorting $sortings->getByKey(self::DEFAULT_SEARCH_SORT);
  343.         if ($defaultSorting !== null) {
  344.             $sortings->remove($defaultSorting->getId());
  345.         }
  346.         $event->getResult()->setAvailableSortings($sortings);
  347.     }        
  348.     private function siteSearch_removeScoreSorting(ProductListingResultEvent $event): void
  349.     {
  350.         $sortings $event->getResult()->getAvailableSortings();
  351.         $defaultSorting $sortings->getByKey(self::DEFAULT_SEARCH_SORT);
  352.         if ($defaultSorting !== null) {
  353.             $sortings->remove($defaultSorting->getId());
  354.         }
  355.         $event->getResult()->setAvailableSortings($sortings);    
  356.     }
  357.     private function getSortParamsFromQuery($param) : array
  358.     {
  359.         $ret=['name'=>'''sort'=>'''key'=>0'fsname'=>''];
  360.         $ret['name']=$param;$ret['fsname']=$param;
  361.         return $ret;
  362.     }
  363.     private function addSortingFromRequest(ProductSearchCriteriaEvent $event) : ProductSearchCriteriaEvent
  364.     {
  365.         return $event;
  366.     }
  367.     private function addSortingFromListingRequest(ProductListingCriteriaEvent $event) : ProductListingCriteriaEvent
  368.     {
  369.         $criteria $event->getCriteria();
  370.         $request $event->getRequest();
  371.         $context $event->getSalesChannelContext();
  372.         if (empty($request->get('order'))) { return $event;}
  373.         $sortkey trim($request->get('order'));
  374.         if ($sortkey != '') {
  375.             $params=$this->getSortParamsFromQuery($sortkey);
  376.             $criteria->resetSorting();
  377.             /** @var ProductSortingCollection $sortings */
  378.             $sortings = new ProductSortingCollection();
  379.             $sn = new ProductSortingEntity();
  380.             $sn->setId(Uuid::randomHex());
  381.             $sn->setFields(
  382.                 [
  383.                     ['field' => '_score''order' => 'desc''priority' => 1'naturalSorting' => 0]
  384.                 ]
  385.                 );
  386.             $sn->setKey('score');
  387.             $sn->setActive(false);
  388.             $sn->setLabel('Beste Ergebnisse');
  389.             $sn->setTranslated(['label'=>'Beste Ergebnisse']);
  390.             $sortings->add($sn);
  391.             $sn = new ProductSortingEntity();
  392.             $sn->setId(Uuid::randomHex());
  393.             $sn->setFields(
  394.                 [
  395.                     ['field' => $sortkey'order' => 'desc''priority' => 1'naturalSorting' => 0]
  396.                 ]
  397.                 );
  398.             $sn->setKey($sortkey);
  399.             $sn->setLabel($sortkey);
  400.             $sn->setActive(true);
  401.             $sn->addExtension('semknoxData', new ArrayEntity(
  402.                 [
  403.                     'params' => $params
  404.                 ]));
  405.             $sn->setTranslated(['label'=>$sortkey]);
  406.             $sortings->add($sn);
  407.             $fsorting $sn->createDalSorting();
  408.             $fsorting[0]->addExtension('semknoxData', new ArrayEntity(
  409.                 [
  410.                     'params' => $params
  411.                 ]));
  412.             $criteria->addSorting(...$fsorting);
  413.             $criteria->addExtension('sortings',  $sortings);
  414.             $event = new ProductSearchCriteriaEvent($request$criteria$context);
  415.         }
  416.         return $event;
  417.     }
  418.     private function addQueryToRequest(Request &$requeststring $add) {
  419.         $query $request->getQueryString();
  420.         $query.=$add;
  421.         $qa parse_query($query);
  422.         $request $request->duplicate($qa);        
  423.     }
  424.     private function changeQueryInRequest(Request &$request, array $add) {
  425.         $query $request->getQueryString();
  426.         $qa parse_query($query);
  427.         foreach ($add as $k => $v) {
  428.             $qa[$k] = $v;
  429.         }
  430.         $request $request->duplicate($qa);
  431.     }
  432.     private function getAvailableSortings(array $semknoxSortData): EntityCollection
  433.     {
  434.         /** @var ProductSortingCollection $sortings */
  435.         $sortings = new ProductSortingCollection ();
  436.         $selected 'score';
  437.         $sn = new ProductSortingEntity();
  438.         $sn->setId(Uuid::randomHex());
  439.         $sn->setFields(
  440.             [
  441.                 ['field' => '_score''order' => 'desc''priority' => 1'naturalSorting' => 0]
  442.             ]
  443.             );
  444.         $sn->setKey('score');
  445.         $sn->setActive(false);
  446.         $sn->setLabel('Beste Ergebnisse');
  447.         $sn->setTranslated(['label'=>'Beste Ergebnisse']);
  448.         $sortings->add($sn);
  449.         foreach ($semknoxSortData as $sd) {
  450.             $sn = new ProductSortingEntity();
  451.             $sn->setId(Uuid::randomHex());
  452.             $fields=[];
  453.             foreach ($sd['fields'] as $fk => $fv) {
  454.                 $f=['field' => $fk'order' => $fv'priority'=>1'naturalSorting' => 0];
  455.                 $fields[]=$f;
  456.             }
  457.             $sn->setFields($fields);
  458.             $sn->setKey($sd['shopKey']);
  459.             $sn->setLabel($sd['name']);
  460.             $sn->setTranslated(['label'=>$sd['name']]);
  461.             $sn->setActive(false);
  462.             if ($sd['selected']) {
  463.                 $sn->setActive(true);
  464.                 $selected $sd['shopKey'];
  465.             }
  466.             $sortings->add($sn);
  467.         }
  468.         if ($selected == 'score') {
  469.             $sortings->first()->setActive(true);
  470.         }
  471.         return $sortings;
  472.     }
  473.     private function getCurrentSorting(ProductSortingCollection $sortingsRequest $request): ProductSortingEntity
  474.     {
  475.         $key $request->get('order');
  476.         foreach($sortings->getElements() as $sorting) {
  477.             if ($sorting->isActive()) {
  478.                 return $sorting;
  479.             }
  480.         }
  481.         throw new ProductSortingNotFoundException($key);
  482.     }
  483.     /**
  484.      * replace the sorting with sitesearch-Sorting
  485.      */
  486.     private function addSortingFromSiteSearch(ProductListingResultEvent $event) : ProductListingResultEvent
  487.     {
  488.         $result $event->getResult();
  489.         $criteria $result->getCriteria();
  490.         $request $event->getRequest();
  491.         $context $event->getSalesChannelContext();
  492.         $semknoxSort $result->getExtension('semknoxResultData');
  493.         if ( ($semknoxSort===null) ) {
  494.             return $event;
  495.         }
  496.         $result->getCriteria()->resetSorting();
  497.         $semknoxData $semknoxSort->getVars();
  498.         if ( (!is_array($semknoxData['data'])) || (!is_array($semknoxData['data']['sortData'])) ) {
  499.             return event;
  500.         }
  501.         $sortings = array();$selected 'score';
  502.         /** @var ProductSortingCollection $sortings */
  503.         $sortings = new ProductSortingCollection();
  504.         $sortings->merge($this->getAvailableSortings($semknoxData['data']['sortData']));
  505.         $currentSorting $this->getCurrentSorting($sortings$request);
  506.         $criteria->addSorting(
  507.             ...$currentSorting->createDalSorting()
  508.             );
  509.         $criteria->addExtension('sortings'$sortings);
  510.         $result->setSorting($currentSorting->getKey());
  511.         $result->setAvailableSortings($sortings);
  512.         $this->changeQueryInRequest($request, ['order' => $currentSorting->getKey()]);
  513.         $event = new ProductListingResultEvent($request$result$context);
  514.         /*
  515.         if (empty($request->get('order'))) {
  516.             $this->addQueryToRequest($request, '&order=score');
  517.         }
  518.         */
  519.         return $event;
  520.     }
  521.     private function getFilterArrayFromRequest(&$qa) : ?array
  522.     {
  523.         $noFilter=['p''order''search''s''page'];
  524.         $ret=null;
  525.         if (!is_array($qa)) { return $ret; }
  526.         $ret=[];$newf=[];
  527.         foreach ($qa as $key => $qitem) {
  528.             $f=0;
  529.             if  (substr($key,0,1)=='_')   {
  530.                 $h=explode('_'$key);
  531.                 if (count($h)>0) {
  532.                     $ha = ['name'=>'''valueList'=>[], 'value'=>'''valType'=>'''minValue'=>'''maxValue'=>''];
  533.                     $ha['name']=$h[1];
  534.                     $ha['value']=''.$qitem;
  535.                     if (count($h)>2) {
  536.                         $ha['valType'] = strtolower($h[2]);
  537.                         if (strtolower($ha['valType'])=='min') {
  538.                             $ha['minValue']=floatval($ha['value']);
  539.                         } else {
  540.                             $ha['maxValue']=floatval($ha['value']);
  541.                         }
  542.                     }
  543.                     if ( (isset($ret[$ha['name']])) && (in_array($ha['valType'], ['min','max'])) ) {
  544.                         if ($ha['valType']=='min') {
  545.                             $ret[$ha['name']]['minValue']=floatval($ha['value']);
  546.                         } else {
  547.                             $ret[$ha['name']]['maxValue']=floatval($ha['value']);
  548.                         }
  549.                     } else {
  550.                         $ret[$ha['name']]=$ha;
  551.                     }
  552.                     $f=1;
  553.                 }
  554.             }
  555.             if (strtolower($key)=='properties') {
  556.                 $x $this->semknoxSearchHelper->getFilterPropertiesList($qitem);
  557.                 foreach ($x as $xqitem) {
  558.                     $h=$this->semknoxSearchHelper->getFilterPropertiesEntity($xqitem);
  559.                     if (count($h)>2) {
  560.                         $ha = ['name'=>'''valueList'=>[], 'valType'=>'list''value'=>''];
  561.                         $ha['name']=$h[1];
  562.                         $ha['valueList'][]=''.$h[2];
  563.                         if (isset($ret[$ha['name']])) {
  564.                             $ret[$ha['name']]['valueList'][]=''.$h[2];
  565.                         } else {
  566.                             $ret[$ha['name']]=$ha;
  567.                         }
  568.                     }
  569.                 }
  570.                 $f=1;
  571.             }
  572.             if ($f==0) {
  573.                 $newf[$key]=$qitem;
  574.             }
  575.         }
  576.         $qa=$newf;
  577.         return $ret;
  578.     }
  579.     private function addFilterFromRequest(ProductSearchCriteriaEvent $event) : ProductSearchCriteriaEvent
  580.     {
  581.         $criteria $event->getCriteria();
  582.         $request $event->getRequest();
  583.         $context $event->getSalesChannelContext();
  584.         $query $request->getQueryString();
  585.         $qa parse_query($query);
  586.         $filterA $this->getFilterArrayFromRequest($qa);
  587.         if ( ($filterA === null) || (empty($filterA)) ) return $event;
  588.         $request $request->duplicate($qa);
  589.         $criteria->addExtension('semknoxDataFilter', new ArrayEntity(
  590.             [
  591.                 'filter' => $filterA
  592.             ]));
  593.         $event = new ProductSearchCriteriaEvent($request$criteria$context);
  594.         return $event;
  595.     }
  596.     private function addFilterFromListingRequest(ProductListingCriteriaEvent $event) : ProductListingCriteriaEvent
  597.     {
  598.         $criteria $event->getCriteria();
  599.         $request $event->getRequest();
  600.         $context $event->getSalesChannelContext();
  601.         $query $request->getQueryString();
  602.         $qa parse_query($query);
  603.         $filterA $this->getFilterArrayFromRequest($qa);
  604.         if ( ($filterA === null) || (empty($filterA)) ) return $event;
  605.         $request $request->duplicate($qa);
  606.         $criteria->addExtension('semknoxDataFilter', new ArrayEntity(
  607.             [
  608.                 'filter' => $filterA
  609.             ]));
  610.         $event = new ProductSearchCriteriaEvent($request$criteria$context);
  611.         return $event;
  612.     }
  613.     private function getPage(Request $request): int
  614.     {
  615.         $page $request->query->getInt('p'1);
  616.         if ($request->isMethod(Request::METHOD_POST)) {
  617.             $page $request->request->getInt('p'$page);
  618.         }
  619.         return $page <= $page;
  620.     }
  621.     private function getLimit(Request $requestSalesChannelContext $context): int
  622.     {
  623.         $limit $request->query->getInt('limit'0);
  624.         if ($request->isMethod(Request::METHOD_POST)) {
  625.             $limit $request->request->getInt('limit'$limit);
  626.         }
  627.         $limit $limit $limit $this->systemConfigService->getInt('core.listing.productsPerPage'$context->getSalesChannel()->getId());
  628.         return $limit <= 24 $limit;
  629.     }
  630.     /**
  631.      * creating interna datasctucture of shopware from the range-values of sitesearch
  632.      * @param array $data
  633.      * @return array
  634.      */
  635.     private function getMinMaxValuesFromFilterData(array $data) : array
  636.     {
  637.         $ret=[];
  638.         foreach ($data as $k => $v) {
  639.             if ($v['type']=='RANGE') {
  640.                 $sum=0;$anz=0;
  641.                 $f=$k;
  642.                 foreach ($v['counts'] as $x => $c) {
  643.                     $sum+=$x*$c;
  644.                     $anz+=$c;
  645.                 }
  646.                 $v['sum']=$sum;
  647.                 $v['avg']=$sum/$anz;
  648.                 if ( (!isset($v['unit'])) || (empty($v['unit'])) ) {
  649.                     $v['unit']='';
  650.                     if (in_array(strtolower($v['name']), ['price','preis','verkaufspreis'])) {
  651.                         $v['unit']='€';
  652.                         $v['conceptType']='PRICE_ATTRIBUTE';
  653.                     }
  654.                 }
  655.                 $v['labelMin'] = $v['name'].' ab';
  656.                 $v['labelMax'] = $v['name'].' bis';
  657.                 $v['labelError'] = 'Der min-Wert von '.$v['name'].' sollte nicht größer sein als der max-Wert!';
  658.                 if (!isset($v['min'])) { $v['min'] = null; }
  659.                 if (!isset($v['max'])) { $v['max'] = null; }
  660.                 if ( (!is_null($v['min'])) && (!is_null($v['max'])) ) {
  661.                     $ret[]=$v;
  662.                 }
  663.             }
  664.             unset($v);
  665.         }
  666.         return $ret;
  667.     }
  668.     private function getFilterPropertyGroupEntity(array $filter) : ?propertyGroupEntity
  669.     {
  670.         $ret null;
  671.         if (! in_array($filter['type'], $this->filterableType)) { return $ret; }
  672.         $pge = new PropertyGroupEntity();
  673.         $pgeID '_'.$filter['name'];
  674.         $pgeIDmm '_'.$filter['name'];
  675.         $pge->setId($pgeID);
  676.         $pge->setName($filter['name']);
  677.         $pge->addTranslated('name'$filter['name']);
  678.         $displType='text';$sortType='position';
  679.         if ($filter['type'] == 'COLOR') {
  680.             $displType='color';
  681.         }
  682.         $pge->setDisplayType($displType);
  683.         $pge->setSortingType($sortType);
  684.         $pge->setDescription('');
  685.         $pgo = new PropertyGroupOptionCollection();
  686.         $pos=0;
  687.         if ($displType == 'numeric') {
  688.             foreach ($filter['counts'] as $k => $v) {
  689.                 $pgoe = new PropertyGroupOptionEntity();
  690.                 $pgoe->setName($k.$filter['unit']);
  691.                 $pgoe->setPosition($pos++);
  692.                 $pgoe->setId($pgeIDmm.'_'.$k);
  693.                 $pgoe->setUniqueIdentifier($pgeIDmm.'_'.$k);
  694.                 $pgoe->addTranslated('name'$k.$filter['unit']);
  695.                 $pgo->add($pgoe);
  696.             }
  697.         }
  698.         if ( ($displType == 'text') || ($displType == 'color') ) {
  699.             foreach ($filter['values'] as $value) {
  700.                 $pgoe = new PropertyGroupOptionEntity();
  701.                 $pgoe->setName($value['name']);
  702.                 $pgoe->setPosition($pos++);
  703.                 $pgoe->setId($pgeID.'_'.$value['value']);
  704.                 $pgoe->setUniqueIdentifier($pgeID.'_'.$value['value']);
  705.                 $pgoe->addTranslated('name'$value['name']);
  706.                 if ( ($filter['type'] == 'COLOR') && (isset($value['color'])) && (!empty($value['color'])) ) {
  707.                     $pgoe->setColorHexCode($value['color']);
  708.                 }
  709.                 $pgo->add($pgoe);
  710.             }
  711.         }
  712.         $pge->setOptions($pgo);
  713.         return $pge;
  714.     }
  715.     private function getFilterPropertiesFromResult(array $data) : EntityResult
  716.     {
  717.         $pgc = new PropertyGroupCollection() ;
  718.         foreach ($data as $filter) {
  719.             $pge $this->getFilterPropertyGroupEntity($filter);
  720.             if ($pge === null) { continue; }
  721.             $pgc->add($pge);
  722.         }
  723.         return new EntityResult('properties'$pgc);
  724.     }
  725.     /**
  726.      * transforming filter from sitesearch to shopware-structure
  727.      * @param ProductListingResult $result
  728.      */
  729.     private function setSiteSearchFilterToResult(ProductListingResult $result) : void
  730.     {
  731.         $semknoxSort $result->getExtension('semknoxResultData');
  732.         if ( ($semknoxSort===null) ) {
  733.             return;
  734.         }
  735.         $stdAggs $result->getAggregations();
  736.         $result->getAggregations()->remove('properties');
  737.         $result->getAggregations()->remove('manufacturer');
  738.         $result->getAggregations()->remove('price');
  739.         $result->getAggregations()->remove('shipping-free');
  740.         $result->getAggregations()->remove('rating');
  741.         $semknoxData $semknoxSort->getVars();
  742.         if ( (!is_array($semknoxData['data'])) || (!is_array($semknoxData['data']['filterData'])) ) {
  743.             return;
  744.         }
  745.         $filterData=$semknoxData['data']['filterData'];
  746.         /*
  747.          * first we get the range-filter from the sets
  748.          * as far as shopware-range-filters are price-filter
  749.          * so we transfer the range-filter to our own structure and using them by our template-vars
  750.          * same for prices with range-filter-attribute
  751.          */
  752.         $prlist $this->getMinMaxValuesFromFilterData($filterData);$filterList=[];
  753.         foreach ($prlist as $pr) {
  754.             if ($pr!==null) {
  755.                 $sr = new StatsResult('_'.$pr['name'], $pr['min'], $pr['max'], $pr['avg'], $pr['sum']);
  756.                 $sr->addExtension('semknoxData', new ArrayEntity(['filter' => $pr]));
  757.                 $filterList[]= ['semkAr'=>$pr'semkSr'=>$sr];
  758.             }
  759.         }
  760.         $result->addExtension('semknoxSearchFilter', new ArrayEntity(
  761.             [
  762.                 'filter' => $filterList
  763.             ]));
  764.         $properties $this->getFilterPropertiesFromResult($filterData);
  765.         $result->getAggregations()->set('properties'$properties);
  766.     }
  767.     private function addCurrentListingFilters(ProductListingResultEvent $event): void
  768.     {
  769.         $result $event->getResult();
  770.         $this->setSiteSearchFilterToResult($result);
  771.     }
  772. }