src/WebBundle/Service/SearchService.php line 392

Open in your IDE?
  1. <?php
  2. namespace WebBundle\Service;
  3. use AdmBundle\Helper\Adm;
  4. use Exception;
  5. use FlexApp\Constant\CatalogConst;
  6. use FlexApp\Constant\TimeConstant;
  7. use FlexApp\DTO\FilterGroupsCatalogDTO;
  8. use FlexApp\DTO\RequestCatalogDTO;
  9. use FlexApp\Helper\MetaHelper;
  10. use FlexApp\Service\Meta\MetaManager;
  11. use FlexApp\Service\RecommendationsAiService;
  12. use FlexApp\ValueObject\LocaleVo;
  13. use Import1CBundle\Helper\v3\BiConst;
  14. use Import1CBundle\Helper\v3\InteriorHelper;
  15. use Symfony\Component\HttpFoundation\RedirectResponse;
  16. use Symfony\Component\HttpFoundation\Response;
  17. use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
  18. use Symfony\Component\Routing\RouterInterface;
  19. use WebBundle\Entity\Factory;
  20. use WebBundle\Entity\FilterEntity;
  21. use WebBundle\Helper\App;
  22. use WebBundle\Helper\LocaleHelper;
  23. use WebBundle\Helper\PathHelper;
  24. use WebBundle\Helper\RequestHelper;
  25. use WebBundle\Helper\StrHelper;
  26. use WebBundle\Helper\UserHelper;
  27. use WebBundle\Repository\ArticleRepository;
  28. use WebBundle\Repository\CollectionAlsoViewedRepository;
  29. use WebBundle\Repository\CollectionRepository;
  30. use WebBundle\Repository\FactoryRepository;
  31. use WebBundle\Repository\FilterRepository;
  32. use WebBundle\Repository\IdeaRepository;
  33. use WebBundle\Repository\InteriorHistoryRepository;
  34. use WebBundle\Repository\InteriorRepository;
  35. /**
  36.  * Сервис обработки поиска (фильтров) в каталоге
  37.  */
  38. class SearchService extends ExtendService
  39. {
  40.     private string $currency;
  41.     private LocaleVo $localeVo;
  42.     // указываем редирект, если требуется
  43.     private ?RedirectResponse $redirect null;
  44.     // параметр рекламы AdWords
  45.     private ?string $gclid null;
  46.     private ?string $html null;
  47.     private FiltersService $filterService;
  48.     /** @required */
  49.     public SliderService $sliderService;
  50.     /** @required */
  51.     public CollectionRepository $collRepo;
  52.     /** @required */
  53.     public FilterRepository $filterRepo;
  54.     /** @required */
  55.     public FactoryRepository $factoryRepository;
  56.     /** @required */
  57.     public ArticleRepository $articleRepo;
  58.     /** @required */
  59.     public InteriorRepository $interiorRepo;
  60.     /** @required */
  61.     public IdeaRepository $ideaRepository;
  62.     /** @required */
  63.     public CollectionAlsoViewedRepository $collectionAlsoViewedRepository;
  64.     /** @required */
  65.     public InteriorHistoryRepository $interiorHistoryRepository;
  66.     private bool $isDebug;
  67.     private RouterInterface $router;
  68.     private ?string $titleHtml null;
  69.     private ?MetaManager $metaManager null;
  70.     private ?bool $isOneFilter null;
  71.     private ?bool $isDesignerStyle null;
  72.     private ?bool $isSort null;
  73.     private ?int $sortId null;
  74.     private array $filtersGTM = [];
  75.     private ?array $result null;
  76.     private ?array $calculate null;
  77.     private ?bool $NoWithArticles false;
  78.     private bool $gCount false;
  79.     private int $limit CatalogConst::PAGE_LIMIT;
  80.     private int $page 1;
  81.     private array $searchFilter = [];
  82.     private ?string $searchKey null;
  83.     private ?string $searchSubKey null;
  84.     private array $searchData = [];
  85.     private $discount null;
  86.     private $top20 null;
  87.     private $amount;
  88.     private string $separator CatalogConst::SEPARATOR;
  89.     public function setFilter(RequestCatalogDTO $requestCatalogDTO, array $searchFilter = []): self
  90.     {
  91.         $this->router App::getRouter();
  92.         $this->localeVo $requestCatalogDTO->getLocaleVo();
  93.         $this->currency LocaleHelper::getCurrency();
  94.         $this->setGclid($requestCatalogDTO->getGclid());
  95.         $this->setPage($requestCatalogDTO->getPage());
  96.         $this->setSearchKey($requestCatalogDTO->getKey());
  97.         $this->setSearchSubKey($requestCatalogDTO->getSubkey());
  98.         $this->addSearchFilter($searchFilter);
  99.         $this->addSearchFilter($requestCatalogDTO->getSendData());
  100.         $this->isDebug $requestCatalogDTO->isDebug();
  101.         $this->filterService $filterService App::getContainer()
  102.             ->get('app.service.filters');
  103.         // Добавляем значения рейтинга не из кеша
  104.         /////////////////////////////////////////////////////////////
  105.         if ($requestCatalogDTO->isAjax()) {
  106.             $this->loadByData($filterService);
  107.             $this->setSearchKey($filterService->getUrlKeyStr());
  108.         } else {
  109.             $this->loadByKeyFilters($filterService$this->getSearchKey(), $this->getSearchSubKey());
  110.         }
  111.         if ($this->isRedirect()) {
  112.             return $this;
  113.         }
  114.         $this->addSearchFilter($filterService->getSearchFilter());
  115.         $this->buildPageParams($filterService);
  116.         return $this;
  117.     }
  118.     /**
  119.      * @param FiltersService $filterService
  120.      *
  121.      * @throws Exception
  122.      */
  123.     public function buildPageParams(FiltersService $filterService)
  124.     {
  125.         $filterGroups $filterService->buildFilterGroupsDTO();
  126.         $titleStr $filterService->buldFiltersToStr($filterGroupsfalsefalse);
  127.         $titleHtml $filterService->buldFiltersToStr($filterGroupsfalsetrue);
  128.         if ($this->isOneFilter()) {
  129.             $titleHtml $this->buildH1($filterService$titleHtml);
  130.         }
  131.         $titleHtml $this->getLinkToAdvancedSearchIfNecessary($titleHtml);
  132.         $this->titleHtml $titleHtml;
  133.         // описание выводим только для каталога
  134.         // https://te.remote.team/#/discus/74928FFE-2BA0-006D-7D3A-6119415936BB/
  135.         if (!$this->isBrands()) {
  136.             $this->html $this->buildHtml($filterService);
  137.         }
  138.         $this->buildFilterForGTM($filterGroups);
  139.         $this->amount $this->setResultAmount($this->getResultCount());
  140.         $metaTitle $titleStr;
  141.         $list $this->getResultList();
  142.         if ($list) {
  143.             $priceMin array_column($list'pr_min');
  144.             $priceMin min($priceMin);
  145.         } else {
  146.             $priceMin 0;
  147.         }
  148.         $currency html_entity_decode($this->currency);
  149.         $this->metaManager MetaHelper::getManager($filterService, [
  150.             '_route' => 'app_catalog',
  151.             'title' => $metaTitle,
  152.             'name' => $titleStr,
  153.             'countResult' => $this->getResultCount(),
  154.             'isOneFilter' => $this->isOneFilter(),
  155.             'min_price' => $priceMin,
  156.             'currency' => $currency,
  157.         ]);
  158.     }
  159.     /**
  160.      * Генерация заголовка для страницы посещения
  161.      *
  162.      * @param FiltersService $filterService
  163.      *
  164.      * @return string
  165.      * @throws Exception
  166.      */
  167.     public function getVisitedTitle(FiltersService $filterService)
  168.     {
  169.         $factoryFilter = isset($this->searchFilter['factory']);
  170.         /** @var FilterEntity $aCurFilter */
  171.         $aCurFilters $filterService->getCurFilters();
  172.         $aCurFilter = (count($aCurFilters) >= 1) ? $aCurFilters[0] : $aCurFilters;
  173.         if (!$aCurFilter) {
  174.             return null;
  175.         }
  176.         $name null;
  177.         if (count($aCurFilters) == and $factoryFilter) {
  178.             if ($brand $aCurFilter->getBrand()) {
  179.                 $name $brand->getName();
  180.             }
  181.         }
  182.         $name $name ?: $this->getMetaManager()
  183.             ->getTitle();
  184.         return $name;
  185.     }
  186.     /**
  187.      * Генерация HTML описания при создании новой записи в SR и SC
  188.      *
  189.      * @param FiltersService $filterService
  190.      *
  191.      * @return mixed|string
  192.      * @throws Exception
  193.      */
  194.     private function buildHtml(FiltersService $filterService)
  195.     {
  196.         $aCurFilters $filterService->getCurFilters();
  197.         $aCurFilter = (count($aCurFilters) >= 1) ? $aCurFilters[0] : $aCurFilters;
  198.         if (!$this->isOneFilter()) {
  199.             return '';
  200.         }
  201.         // если фильтр 1 и это сортировка, то выводим описание главной каталога
  202.         if ($this->isSort() and $aCurFilter->isSort()) {
  203.             return '';
  204.         }
  205.         if (!$aCurFilter->isHtmlShow()) {
  206.             return '';
  207.         }
  208.         $html $aCurFilter->getHtml();
  209.         return LocaleHelper::modifeLinkForLocales($html);
  210.     }
  211.     /**
  212.      * Определяем сколько фильтров будет отображено на странице в заголовке
  213.      * @return bool
  214.      * @throws Exception
  215.      */
  216.     public function isOneFilter(): ?bool
  217.     {
  218.         if ($this->isOneFilter === null) {
  219.             $aCurFilters $this->filterService()
  220.                 ->getCurFilters();
  221.             $countFilters count($aCurFilters);
  222.             if ($countFilters 1) {
  223.                 foreach ($aCurFilters as $oCurFilter) {
  224.                     if ($oCurFilter->isSort()) {
  225.                         $countFilters--;
  226.                     } elseif (!$oCurFilter->isEnable() and !$oCurFilter->getBrand()) {
  227.                         $countFilters--;
  228.                     }
  229.                 }
  230.             }
  231.             $this->isOneFilter = ($countFilters == 1);
  232.         }
  233.         return $this->isOneFilter;
  234.     }
  235.     /**
  236.      * массив для GTM
  237.      * https://te.remote.team/#/discus/0CA30156-F404-63A9-2515-38E75FB7947B/
  238.      *
  239.      * @param FilterGroupsCatalogDTO $filterGroups
  240.      */
  241.     public function buildFilterForGTM(FilterGroupsCatalogDTO $filterGroups)
  242.     {
  243.         $arr = [];
  244.         foreach ($filterGroups->getGroups() as $gr) {
  245.             $grAltName $gr->getGroupAltName();
  246.             if (empty($arr[$grAltName])) {
  247.                 $arr[$grAltName] = [];
  248.             }
  249.             foreach ($gr->getListFilters() as $f) {
  250.                 $arr[$grAltName][] = [
  251.                     'id' => $f->getId(),
  252.                     'keyId' => $f->getKeyId(),
  253.                     'key' => $f->getKey(),
  254.                     'group' => $grAltName,
  255.                     'altName' => $f->getAltName(),
  256.                 ];
  257.             }
  258.         }
  259.         $this->filtersGTM $arr;
  260.     }
  261.     /**
  262.      * массив для GTM
  263.      * https://te.remote.team/#/discus/0CA30156-F404-63A9-2515-38E75FB7947B/
  264.      * @return array
  265.      */
  266.     public function getFiltersGTM()
  267.     {
  268.         return $this->filtersGTM;
  269.     }
  270.     /**
  271.      * @param $data
  272.      * @param $tpl
  273.      *
  274.      * @return string|Response
  275.      * @throws Exception
  276.      */
  277.     private function renderH1($data$tpl)
  278.     {
  279.         switch ($tpl) {
  280.             case 'factory-header':
  281.                 $result $this->render(
  282.                     '@Web/Catalog/factory-header.html.twig',
  283.                     $data
  284.                 );
  285.                 break;
  286.             case 'one-header':
  287.                 $result $this->render(
  288.                     '@Web/Catalog/one-header.html.twig',
  289.                     $data
  290.                 );
  291.                 break;
  292.             case 'new-header':
  293.                 $result $this->render(
  294.                     '@Web/Catalog/new-header.html.twig',
  295.                     $data
  296.                 );
  297.                 break;
  298.             case 'header':
  299.                 $result $this->render(
  300.                     '@Web/Catalog/header.html.twig',
  301.                     $data
  302.                 );
  303.                 break;
  304.             default:
  305.                 $result $this->render(
  306.                     '@Web/Catalog/one-header.html.twig',
  307.                     $data
  308.                 );
  309.         }
  310.         $result htmlspecialchars_decode($result);
  311.         return $result;
  312.     }
  313.     /**
  314.      * @param FiltersService $filterService
  315.      * @param $title
  316.      *
  317.      * @return mixed|string|Response
  318.      * @throws Exception
  319.      */
  320.     private function buildH1(FiltersService $filterService$title)
  321.     {
  322.         $aCurFilters $filterService->getCurFilters();
  323.         $aCurFilter = (count($aCurFilters) >= 1) ? $aCurFilters[0] : $aCurFilters;
  324.         if ($this->isOneFilter()) {
  325.             if ($aCurFilter->isBrand()) {
  326.                 $brand $aCurFilter->getBrand();
  327.                 $countryName '';
  328.                 $countryUrl '';
  329.                 if ($brand->getCountry()) {
  330.                     $countryName $brand->getCountry()
  331.                         ->getName();
  332.                     $countryUrl $this->generateUrl(
  333.                         'app_catalog',
  334.                         [
  335.                             'key' => $brand->getCountry()
  336.                                 ->getSlug()
  337.                         ]
  338.                     );
  339.                 }
  340.                 $data = [
  341.                     'factory' => $brand,
  342.                     'typeKey' => Factory::TYPES_KEY[$brand->getType()],
  343.                     'country' => [
  344.                         'name' => $countryName,
  345.                         'url' => $countryUrl,
  346.                     ],
  347.                     'editLink' => Adm::linkEdit('adm.brand.edit'$aCurFilter->getId()),
  348.                     'biLink' => Adm::linkEdit('bi.factory.edit'$brand->getId()),
  349.                 ];
  350.                 $h1 $this->renderH1($data'factory-header');
  351.             } elseif ($this->isSort()) {
  352.                 $isTop false;
  353.                 if (RequestHelper::getCurRoute() == 'app_catalog') {
  354.                     $isTop = (!$this->isTop('also_coll_viewed') and $this->isTop()) ? $this->isTop() : false;
  355.                 }
  356.                 $h1 $this->renderH1(['header' => $title'isTop' => $isTop], 'one-header');
  357.             } elseif ($aCurFilter->isRootCataloge()) {
  358.                 $h1 $this->renderH1(['header' => $title], 'new-header');
  359.             } else {
  360.                 $isTop false;
  361.                 if (RequestHelper::getCurRoute() == 'app_catalog') {
  362.                     $isTop = (!$this->isTop('also_coll_viewed') and $this->isTop()) ? $this->isTop() : false;
  363.                 }
  364.                 $h1 $this->renderH1(['header' => $title'isTop' => $isTop], 'one-header');
  365.             }
  366.         } else {
  367.             // оборачиваем $title в описание строки фильтрации
  368.             $h1 $this->setCountResultNew($title$this->getResultCount());
  369.         }
  370.         return $h1;
  371.     }
  372.     /**
  373.      * Поиск по Массиву значений старого образца вида: ['getColors'=>[3], 'getFacturas'=>[5]]
  374.      * @throws Exception
  375.      */
  376.     private function loadByData(FiltersService $filterService)
  377.     {
  378.         if (!$this->getSearchFilter()) {
  379.             // залипуха для главной каталога
  380.             $filterService->loadByUrlKey('cataloge'$this->localeVo->getCode());
  381.         } else {
  382.             // загружаем данные строки в FiltersService
  383.             $filterService->loadBySearchData($this->getSearchFilter());
  384.             // из результата получаем новый searchFilter
  385.             $searchFilter $filterService->getSearchFilter();
  386.             $this->setSearchFilter($searchFilter);
  387.         }
  388.     }
  389.     /**
  390.      * @param FiltersService $filterService
  391.      * @param string|null $key
  392.      * @param string|null $subKey
  393.      *
  394.      * @return bool
  395.      * @throws Exception
  396.      */
  397.     private function loadByKeyFilters(FiltersService $filterService, ?string $key null, ?string $subKey null)
  398.     {
  399.         $lc $this->localeVo->getCode();
  400.         // залипуха для главной каталога
  401.         if (!$key) {
  402.             $filterService->loadByUrlKey('cataloge'$lc);
  403.             return true;
  404.         }
  405.         $keyNorm $this->normaliseKey($key);
  406.         $keyNorm $subKey $keyNorm $this->separator $subKey $keyNorm;
  407.         // доп залипуха для главной каталога //$keyNorm = 'cataloge';
  408.         if (!$keyNorm) {
  409.             throw new NotFoundHttpException('Not Found');
  410.         }
  411.         // фикс для "бажных" ссылок, вида /fi_it/catalogue/coll/en
  412.         // отправляем их на главную каталога
  413.         if ($keyNorm == 'coll&en') {
  414.             $newUrl $this->router->generate('app_catalog');
  415.             $this->setRedirect($newUrl301);
  416.             return false;
  417.         }
  418.         // загружаем данные строки в FiltersService
  419.         $filterService->loadByUrlKey($keyNorm$lc);
  420.         // из результата получаем новый key строки
  421.         $newKey $filterService->getUrlKeyStr();
  422.         if (!$newKey) {// на каталог больше не перенаправляем
  423.             throw new NotFoundHttpException('Not Found');
  424.         }
  425.         if (
  426.             (strlen($newKey) != strlen($key)) or (strlen($newKey) <= and strlen($key)) or (substr_compare(
  427.                 $key,
  428.                 $newKey,
  429.                 0,
  430.                 strlen($key)
  431.             ) != 0)
  432.         ) {
  433.             // 301 редирект на верную страницу
  434.             $newUrl $filterService->getFullUrlKeyStr($newKey);
  435.             $gclid $this->getGclid();
  436.             if ($gclid) {
  437.                 $newUrl $newUrl '?gclid=' $gclid;
  438.             }
  439.             // формирование верного редиректа для коллекций
  440.             $request App::getRequest();
  441.             if ($request->get('_route') == 'app_collection') {
  442.                 $routeParams array_merge($request->get('_route_params'), ['elementId' => $newKey]);
  443.                 $newUrl str_replace('%26''&'$this->generateUrl('app_collection'$routeParams));
  444.             }
  445.             $this->setRedirect($newUrl301);
  446.             return false;
  447.         }
  448.         return true;
  449.     }
  450.     /**
  451.      * @return FiltersService
  452.      */
  453.     public function filterService(): FiltersService
  454.     {
  455.         return $this->filterService;
  456.     }
  457.     /////////////////////////////////////////////////////////////////////////
  458.     ///геттеры и сеттеры
  459.     /////////////////////////////////////////////////////////////////////////
  460.     /**
  461.      * Получаем количество найденных резильтатов поиска из сфинкса
  462.      * @return mixed
  463.      * @throws Exception
  464.      */
  465.     public function getResultCount()
  466.     {
  467.         $result $this->getResult();
  468.         return $result['count'];
  469.     }
  470.     /**
  471.      * Получаем количество найденных резильтатов поиска из сфинкса
  472.      * @return mixed
  473.      * @throws Exception
  474.      */
  475.     public function getResultList()
  476.     {
  477.         $result $this->getResult();
  478.         return $result['list'];
  479.     }
  480.     /**
  481.      * Результат из сфинкса берем
  482.      * @return array
  483.      * @throws Exception
  484.      */
  485.     public function getResult(): ?array
  486.     {
  487.         if ($this->result !== null) {
  488.             return $this->result;
  489.         }
  490.         $data $this->getSearchData();
  491.         if ($this->isTop('top20month') || $this->isTop('top_month')) {
  492.    //         $data['searchFilter']['c_id'] = $this->getSearchTopMonth();
  493.         } elseif ($this->isTop('top_week')) {
  494.      //       $data['searchFilter']['c_id'] = $this->getSearchTopWeek();
  495.         }
  496. //APP::debugExit($data);
  497.         $limit $this->getLimit();
  498.         $page $this->getPage();
  499.         if ($this->isTop('also_coll_viewed')) {
  500.             $data['searchFilter']['c_id'] = $this->getAlsoCollViewed();
  501.         }
  502.         $oMemcache App::getMemcache();
  503.         $_measure LocaleHelper::getUserMeasure();
  504.         $_sort 'sort_' $this->getSearchSortId();
  505.         $view App::getRequest()->cookies->get('view_catalog'1);
  506.         $test App::isRole('ROLE_TEST') ? '_test' '';
  507.         $keyCache 'collection_filter_5' $this->localeVo->getCodeFull() . $this->currency $_measure md5(
  508.             json_encode($data)
  509.         ) . $limit $page $_sort App::getRequest()
  510.                 ->get('items') . $view $test;
  511.         $keyCacheAll 'collection_filter_5' $this->localeVo->getCodeFull() . $this->currency $_measure md5(
  512.             json_encode($data)
  513.         ) . $view $_sort;
  514.         $result $oMemcache->get($keyCache);
  515.         // для проверки без кеша в запрос добавляем ?debug=1
  516.         if (!$result || $this->isDebug) {
  517.             $min = ($limit $page) - $limit;
  518.             $max $limit $page;
  519.             if (!empty($data['searchFilter']['c_id'])) {
  520.                 $sphinxResult_cIds['c_ids'] = $data['searchFilter']['c_id'];
  521.                 $sphinxResult_cIds['count'] = count($data['searchFilter']['c_id']);
  522.                 if (!empty($data['searchFilter']['inside'])) {
  523.                     $limit 0;
  524.                 }
  525.             } else {
  526.                 $limit 3;
  527.                 $sphinxResult_cIds $oMemcache->get($keyCacheAll);
  528.             }      //если нет списка всех коллекций - то берем все данные выборки
  529.             $resTmp = [];
  530.             if (empty($sphinxResult_cIds)) {
  531.                 $c_id_data $data;
  532.                 $c_id_data['only_c_id'] = 1;
  533.                 $sphinxResult App::searchSphinx($c_id_data1falsetrue$this->gCounttrue$this->NoWithArticles);
  534.                 //так как получили количество то
  535.                 if ($this->gCount) {
  536.                     $allCalculate $sphinxResult['res']['calculate'] ?? $sphinxResult['calculate'];
  537.                     $this->setCalculate($allCalculate);
  538.                     $oMemcache->set($keyCache 'calc'$allCalculateMEMCACHE_COMPRESSED, (int)TimeConstant::DAY);
  539.                     $res $sphinxResult['res']['collections'] ?? $sphinxResult['collections'];
  540.                 } else {
  541.                     $res $sphinxResult['res'] ?? $sphinxResult;
  542.                 }
  543.                 $c_ids array_column($res'c_id');
  544.                 $sphinxResult_cIds = ['c_ids' => $c_ids'count' => count($res)];
  545.                 $oMemcache->set($keyCacheAll$sphinxResult_cIdsMEMCACHE_COMPRESSED, (int)TimeConstant::DAY);
  546.             }
  547.             if (1) {
  548.                 // уже есть то берем только нужную порцию если не выбрана уже
  549.                 if (empty($data['searchFilter']['c_id'])) {
  550.                     $data['searchFilter']['c_id'] = array_slice($sphinxResult_cIds['c_ids'], $min$max $min);
  551.                 }
  552.                 $count $sphinxResult_cIds['count'];
  553.                 $sphinxResult App::searchSphinx($data$limitfalsetrue$this->gCountfalse$this->NoWithArticles);
  554.                 //так как получили количество то
  555.                 if ($this->gCount) {
  556.                     $res $sphinxResult['res']['collections'] ?? $sphinxResult['collections'];
  557.                 } else {
  558.                     $res $sphinxResult['res'] ?? $sphinxResult;
  559.                 }
  560.                 foreach ($res as $i => $item) {
  561.                     $discontinued false;
  562.                     if ((!empty($data['c_satus']) && $data['c_satus'] == BiConst::STATE_DISCONTINUED)) {
  563.                         $discontinued true;
  564.                     }
  565.                     $item['discontinued'] = $discontinued;
  566.                     if ($item['author']) {
  567.                         $item['author'] = str_replace(','', '$item['author']);
  568.                     }
  569.                     $resTmp[$i] = $item;
  570.                 }
  571.             }
  572.             $recommendationsAiService = new RecommendationsAiService();
  573.             foreach ($resTmp as $i => $r) {
  574.                 $collId $r['c_id'];
  575.                 // получение ALT для интерьеров
  576.                 if (!empty($r['interiors'])) {
  577.                     $interiorIds array_column($r['interiors'], 'i_id');
  578.                     $interiorsValue $this->interiorRepo->getInteriorsForCollection($collId, ['ids' => $interiorIds]);
  579.                     $interiorsValue array_combine(array_column($interiorsValue'id'), $interiorsValue);
  580.                     $dataColl = [
  581.                         'url' => $r['c_url'],
  582.                         'header' => $r['c_header'],
  583.                         'name' => $r['c_name'],
  584.                         'alternateName' => $r['c_name'],
  585.                         'factory' => [
  586.                             'url' => $r['f_url'],
  587.                             'alternateName' => $r['f_name'],
  588.                         ],
  589.                     ];
  590.                     foreach ($r['interiors'] as $ii => $interior) {
  591.                         $alt null;
  592.                         if (!empty($interiorsValue[$interior['i_id']])) {
  593.                             $interiorValue $interiorsValue[$interior['i_id']];
  594.                             $interiorAlt InteriorHelper::getInteriorDetails($interiorValue$dataColl$ii);
  595.                             $alt $interiorAlt['alt'];
  596.                             $alt is_array($alt) ? array_shift($alt) : $alt;
  597.                         }
  598.                         $resTmp[$i]['interiors'][$ii]['alt'] = $alt;
  599.                     }
  600.                 }
  601.                 // для артикулов добавляем аттрибуты
  602.                 if (!empty($r['articles'])) {
  603.                     $articleIds array_column($r['articles'], 'a_id');
  604.                     $recArticles $recommendationsAiService->getArticles(['ids' => $articleIds]);
  605.                     $recArticles array_combine(array_column($recArticles'id'), $recArticles);
  606.                     if (!empty($recArticles)) {
  607.                         foreach ($r['articles'] as $ia => $article) {
  608.                             $categories null;
  609.                             $attributes null;
  610.                             if (!empty($recArticles[$article['a_id']])) {
  611.                                 $recArticleValue $recArticles[$article['a_id']];
  612.                                 $recArticleAttr $recommendationsAiService->buildArticleJson(
  613.                                     $recArticleValue,
  614.                                     ['code' => $this->localeVo->getCode()]
  615.                                 );
  616.                                 $categories $recArticleAttr['categories'];
  617.                                 $attributes $recArticleAttr['attributes'];
  618.                             }
  619.                             $resTmp[$i]['articles'][$ia]['categories'] = $categories;
  620.                             $resTmp[$i]['articles'][$ia]['attributes'] = $attributes;
  621.                         }
  622.                     }
  623.                 }
  624.             }
  625.             // собираем сами id артикулов порции
  626.             if (
  627.                 App::getRequest()
  628.                 ->get('items')
  629.             ) {
  630.                 $aIdea = [];
  631.                 foreach ($resTmp as $r) {
  632.                     foreach ($r['articles'] as $i) {
  633.                         $aIdea[] = $i['a_id'];
  634.                     }
  635.                 }
  636.                 $items $this->articleRepo->getArticleNativeOpt(['items' => $aIdea]);
  637.                 $items $this->sliderService->prepareDiscountAmountForEachArticle($items);
  638.                 foreach ($resTmp as $k => $r) {
  639.                     foreach ($r['articles'] as $n => $i) {
  640.                         $resTmp[$k]['articles'][$n] = array_merge($i$items[$i['a_id']]);
  641.                     }
  642.                 }
  643.             }
  644.             $result = [
  645.                 'count' => $count,
  646.                 'list' => $resTmp,
  647.                 'view' => $view,
  648.             ];
  649.             // данные по отзывам коллекций фабрики
  650.             if ($page <= && $this->isBrands() && $this->isOneFilter() && $res) {
  651.                 $result['fName'] = $res[0]['f_name'];
  652.                 $result['fLogo'] = PathHelper::pathGenerate('factory', ['url' => $res[0]['f_url']]);
  653.             }
  654.             // данные по отзывам коллекций фабрики
  655.             if ($page <= && $this->showReview()) {
  656.                 $result['fReview'] = $this->get('app.service.reviews')
  657.                     ->getReviewForFactory($sphinxResult['codes'] ?? []);
  658.             }
  659.             // кеш на сутки для первой страницы
  660.             if ($page <= 1) {
  661.                 $oMemcache->set($keyCache$resultMEMCACHE_COMPRESSED, (int)TimeConstant::DAY);
  662.             }
  663.         } else {
  664.             $calculate $oMemcache->get($keyCache 'calc');
  665.             if ($calculate) {
  666.                 $this->setCalculate($calculate);
  667.             }
  668.         }
  669.         $ids = [];
  670.         foreach ($result['list'] as $item) {
  671.             if (is_numeric($item['c_id'])) {
  672.                 $ids[] = (int)$item['c_id'];
  673.             }
  674.         }
  675.         $urlAttr = [];
  676.         // прикручиваем строки фильтрации к URL коллекции
  677.         $fKey $this->getSearchKey();
  678.         if ($fKey) {
  679.             $urlAttr['type'] = 'f';
  680.             $urlAttr['elementId'] = $fKey;
  681.         }
  682.         $aColl $this->collRepo->getRatings($ids);
  683.         foreach ($result['list'] as $i => $item) {
  684.             $urlAttr['factoryUrl'] = $item['f_url'];
  685.             $urlAttr['collectionUrl'] = $item['c_url'];
  686.             // убираем из фильтров, когда фильтрация по коллекции с которыми смотрят
  687.             // например /**/catalogue/coll/3245167
  688.             if ($fKey == 'coll') {
  689.                 unset($urlAttr['type']);
  690.                 unset($urlAttr['elementId']);
  691.             }
  692.             // убираем фабрику из фильтров внутри коллекции
  693.             if ($this->isBrands() && $this->isOneFilter()) {
  694.                 unset($urlAttr['type']);
  695.                 unset($urlAttr['elementId']);
  696.             }
  697.             $url App::generateUrl('app_collection'$urlAttr);
  698.             $result['list'][$i]['url'] = $url;
  699.             $rating 0;
  700.             if (!empty($aColl[$item['c_id']])) {
  701.                 $rating $aColl[$item['c_id']];
  702.             }
  703.             $result['list'][$i]['rating'] = $rating;
  704.             // проверяем наличие наград и получаем по ним данные, если есть
  705.             $result['list'][$i]['awards'] = [];
  706.             if (
  707.                 !empty($item['c_awards']) && is_array(
  708.                     $item['c_awards']
  709.                 )
  710.             ) {// #todo добавил проверку на array так как была ошибка, но надо разобраться будет
  711.                 $aAwards $this->filterRepo->getAwards($item['c_awards']);
  712.                 if ($aAwards) {
  713.                     $result['list'][$i]['awards'] = $aAwards;
  714.                 }
  715.             }
  716.             // проверяем наличие выставок и получаем по ним даныне, если есть
  717.             $exhibitions = [];
  718.             $result['list'][$i]['c_exhibition'] = $exhibitions;
  719.         }
  720.         /////////////////////////////////////////////////////////////
  721.         // данные по интерьерам добавленым в идеи пользователя
  722.         $token UserHelper::getInstance()
  723.             ->getToken();
  724.         $result['favorites'] = $this->ideaRepository->getInteriorsIdeasByToken($token);
  725.         ////////////////////////////////////////////////////////////
  726.         /// группируем по фабрикам
  727.         $translator App::getTranslator();
  728.         $groups = [];
  729.         foreach ($result['list'] as $item) {
  730.             $fName $item['f_name'];
  731.             $fUrl $this->generateUrl('app_catalog', ['key' => StrHelper::toLower($item['f_url'])]);
  732.             if (empty($groups[$fName])) {
  733.                 $countryCode strtolower($item['country_code']);
  734.                 $countryUrl $this->generateUrl('app_catalog', ['key' => $countryCode]);
  735.                 $grop = [
  736.                     'f_name' => $item['f_name'],
  737.                     'f_url' => $fUrl,
  738.                     'country' => [
  739.                         'code' => $countryCode,
  740.                         'name' => $translator->trans("country.$countryCode"),
  741.                         'url' => $countryUrl,
  742.                     ],
  743.                     'list' => [],
  744.                 ];
  745.                 $groups[$fName] = $grop;
  746.             }
  747.             $header $translator->trans(
  748.                 $item['c_header'],
  749.                 [
  750.                     '%collection%' => preg_replace('#\\(.*\\)#isUu'''$item['c_name']),
  751.                     '%factory%' => $fName,
  752.                     '%factoryUrl%' => $fUrl,
  753.                 ]
  754.             );
  755.             $item['header_html'] = $header;
  756.             $groups[$fName]['list'][] = $item;
  757.         }
  758.         $result['groups'] = $groups;
  759.         $this->result $result;
  760.         return $this->result;
  761.     }
  762.     /**
  763.      * @return int
  764.      * @throws Exception
  765.      */
  766.     public function getLimit()
  767.     {
  768.         if ($this->isTop('also_coll_viewed')) {
  769.             $this->limit 16;
  770.         }
  771.         return $this->limit;
  772.     }
  773.     /**
  774.      * @param mixed $limit
  775.      */
  776.     public function setLimit($limit)
  777.     {
  778.         if ($limit && is_numeric($limit)) {
  779.             $this->limit $limit;
  780.         }
  781.     }
  782.     /**
  783.      * Получение списка сортировки для каталога
  784.      * @return array
  785.      * @throws Exception
  786.      */
  787.     public function getSortList()
  788.     {
  789.         /** @var FilterRepository $filterRepo */
  790.         $filterRepo App::getRepository('WebBundle:FilterEntity');
  791.         $list $filterRepo->getSortListCataloge();
  792.         return $list;
  793.     }
  794.     /**
  795.      * @return int
  796.      * @throws Exception
  797.      */
  798.     public function getSearchSortId()
  799.     {
  800.         if ($this->sortId == null) {
  801.             $sort App::getSession()
  802.                 ->get('sort_catalog_tmp');
  803.             if ($sort == null) {
  804.                 $sort App::getRequest()
  805.                     ->get('sort');
  806.             }
  807.             if ($sort == null) {
  808.                 $sort App::getRequest()->cookies->get('sort_catalog');
  809.             }
  810.             if ($sort == null) {
  811.                 $sort 1;
  812.             }
  813.             $this->sortId $sort;
  814.         }
  815.         return $this->sortId;
  816.     }
  817.     public function getMetaManager(): ?MetaManager
  818.     {
  819.         return $this->metaManager;
  820.     }
  821.     /**
  822.      * @return mixed
  823.      */
  824.     public function getAmount()
  825.     {
  826.         return $this->amount;
  827.     }
  828.     /**
  829.      * Получаем H1 заголовок
  830.      */
  831.     public function getTitleHtml(): ?string
  832.     {
  833.         return $this->titleHtml;
  834.     }
  835.     /**
  836.      * получаем Html описание
  837.      */
  838.     public function getHtml(): ?string
  839.     {
  840.         return $this->html;
  841.     }
  842.     /**
  843.      * @return null
  844.      * @throws Exception
  845.      */
  846.     public function getDiscount()
  847.     {
  848.         if ($this->discount === null) {
  849.             $discount false;
  850.             $factory $this->factoryRepository->findOneBy(
  851.                 ['url' => $this->getSearchKey()]
  852.             );
  853.             if (
  854.                 $factory && $factory->getDiscount() > && $factory->getDiscountDateStart(
  855.                 ) != null && $factory->getDiscountDateEnd() != null && $factory->getDiscountDateStart()
  856.                     ->getTimestamp() <= strtotime(date('d.m.Y')) && $factory->getDiscountDateEnd()
  857.                     ->getTimestamp() > strtotime(date('d.m.Y'))
  858.             ) {
  859.                 $discount true;
  860.             }
  861.             $this->setDiscount($discount);
  862.         }
  863.         return $this->discount;
  864.     }
  865.     /**
  866.      * @param mixed $discount
  867.      */
  868.     public function setDiscount($discount)
  869.     {
  870.         $this->discount $discount;
  871.     }
  872.     /**
  873.      * @return mixed
  874.      */
  875.     public function getTop20()
  876.     {
  877.         return $this->top20;
  878.     }
  879.     /**
  880.      * @param mixed $top20
  881.      */
  882.     public function setTop20($top20)
  883.     {
  884.         $this->top20 $top20;
  885.     }
  886.     /**
  887.      * @return int
  888.      */
  889.     public function getPage()
  890.     {
  891.         return $this->page;
  892.     }
  893.     /**
  894.      * @param mixed $page
  895.      */
  896.     public function setPage($page)
  897.     {
  898.         if ($page && is_numeric($page)) {
  899.             $this->page $page;
  900.         }
  901.     }
  902.     /**
  903.      * @return mixed
  904.      */
  905.     public function getSearchFilter()
  906.     {
  907.         return $this->searchFilter;
  908.     }
  909.     /**
  910.      * @param mixed $searchFilter
  911.      */
  912.     public function setSearchFilter($searchFilter)
  913.     {
  914.         if ($searchFilter) {
  915.             $this->searchFilter $searchFilter;
  916.         }
  917.     }
  918.     /**
  919.      * @param $searchFilter
  920.      */
  921.     public function addSearchFilter($searchFilter)
  922.     {
  923.         if ($searchFilter) {
  924.             if (count($this->searchFilter) > 0) {
  925.                 $this->searchFilter array_merge($this->searchFilter$searchFilter);
  926.             } else {
  927.                 $this->searchFilter $searchFilter;
  928.             }
  929.         }
  930.     }
  931.     /**
  932.      * Получение ссылки на редактирование фильтра через сонату
  933.      * @return bool
  934.      * @throws Exception
  935.      */
  936.     public function isNoindex(): bool
  937.     {
  938.         if ($this->isSort()) {
  939.             // не реализовано
  940.             return true;
  941.         }
  942.         return false;
  943.     }
  944.     /**
  945.      * @param bool $asJson
  946.      *
  947.      * @return array|false|string
  948.      * @throws Exception
  949.      */
  950.     public function getSearchData($asJson false)
  951.     {
  952.         $this->searchData = [
  953.             'searchFilter' => $this->getSearchFilter(),
  954.             'searchSort' => $this->getSearchSortId(),
  955.             'searchPeriod' => null,
  956.             'locale' => $this->localeVo->getCode(),
  957.         ];
  958.         $searchData $asJson json_encode($this->searchDatatrue) : $this->searchData;
  959.         return $searchData;
  960.     }
  961.     /**
  962.      * @param array $searchData
  963.      */
  964.     public function setSearchData($searchData)
  965.     {
  966.         if ($searchData) {
  967.             $this->searchData $searchData;
  968.         }
  969.     }
  970.     /**
  971.      * @return null|RedirectResponse
  972.      */
  973.     public function getRedirect(): ?RedirectResponse
  974.     {
  975.         return $this->redirect;
  976.     }
  977.     /**
  978.      * @return mixed
  979.      */
  980.     public function isRedirect()
  981.     {
  982.         return $this->redirect;
  983.     }
  984.     /**
  985.      * @param     $url
  986.      * @param int $status
  987.      *
  988.      * @throws Exception
  989.      * @internal param mixed $redirect
  990.      */
  991.     public function setRedirect($urlint $status 302)
  992.     {
  993.         $this->redirect = new RedirectResponse($url$status);
  994.         //  пишем лог, если это не ридирект с sort-tmp переменной или дизайнерского стиля
  995.         if (!RequestHelper::get('sort-tmp')) {
  996.             $this->writeLogRedirect($this->redirect->getTargetUrl());
  997.         }
  998.     }
  999.     /**
  1000.      * @return string|null
  1001.      */
  1002.     public function getSearchKey()
  1003.     {
  1004.         return $this->searchKey;
  1005.     }
  1006.     /**
  1007.      * @param $searchKey
  1008.      */
  1009.     public function setSearchKey($searchKey)
  1010.     {
  1011.         if ($searchKey) {
  1012.             $this->searchKey $searchKey;
  1013.         }
  1014.     }
  1015.     /**
  1016.      * @return null
  1017.      */
  1018.     public function getSearchSubKey()
  1019.     {
  1020.         return $this->searchSubKey;
  1021.     }
  1022.     /**
  1023.      * @param $searchSubKey
  1024.      */
  1025.     public function setSearchSubKey($searchSubKey)
  1026.     {
  1027.         if ($searchSubKey) {
  1028.             $this->searchSubKey $searchSubKey;
  1029.         }
  1030.     }
  1031.     /**
  1032.      * @return array
  1033.      */
  1034.     public function getReplaceParentWithChildren(): array
  1035.     {
  1036.         $dataFilterSearch $this->searchFilter;
  1037.         try {
  1038.             $parent $this->filterRepo->getFiltersSubRelation(false);
  1039.         } catch (\Doctrine\DBAL\Exception $e) {
  1040.             $parent = [];
  1041.         }
  1042.         foreach ($dataFilterSearch as $key => &$values) {
  1043.             // Проверка: существует ли такой ключ в $Parent и он является массивом
  1044.             if (isset($parent[$key]) && is_array($parent[$key])) {
  1045.                 foreach ($values as $val) {
  1046.                     // Если значение существует как ключ в $Parent[$key]
  1047.                     if (array_key_exists($val$parent[$key])) {
  1048.                         //удаляем родителя
  1049.                         $dataFilterSearch[$key] = array_diff($dataFilterSearch[$key], [$val]);
  1050.                         //добавляем детей
  1051.                         $dataFilterSearch[$key] = array_merge($dataFilterSearch[$key], $parent[$key][$val]);
  1052.                     }
  1053.                 }
  1054.             }
  1055.         }
  1056.         return $dataFilterSearch;
  1057.     }
  1058.     public function getCalculate(): ?array
  1059.     {
  1060.         return $this->calculate;
  1061.     }
  1062.     public function setCalculate(?array $calculate)
  1063.     {
  1064.         if ($calculate) {
  1065.             $this->calculate $calculate;
  1066.         }
  1067.     }
  1068.     public function setGCount(bool $gCount)
  1069.     {
  1070.         $this->gCount $gCount;
  1071.     }
  1072.     public function getGCount(): bool
  1073.     {
  1074.         return $this->gCount;
  1075.     }
  1076.     public function setNoWithArticles(bool $NoWithArticles): void
  1077.     {
  1078.         $this->NoWithArticles $NoWithArticles;
  1079.     }
  1080.     public function getNoWithArticles()
  1081.     {
  1082.         return $this->NoWithArticles;
  1083.     }
  1084.     /**
  1085.      * Проверка на дизайнерский стиль
  1086.      */
  1087.     public function isDesignerStyle(): ?bool
  1088.     {
  1089.         if ($this->isDesignerStyle === null) {
  1090.             $this->isDesignerStyle false;
  1091.             $aCurFilters $this->filterService()
  1092.                 ->getCurFilters();
  1093.             if (count($aCurFilters) > 0) {
  1094.                 foreach ($aCurFilters as $curFilter) {
  1095.                     if ($curFilter->isDesignerStyle()) {
  1096.                         $this->isDesignerStyle true;
  1097.                         break;
  1098.                     }
  1099.                 }
  1100.             }
  1101.         }
  1102.         return $this->isDesignerStyle;
  1103.     }
  1104.     /**
  1105.      * Проверка на сортировку
  1106.      */
  1107.     public function isSort(): bool
  1108.     {
  1109.         return $this->isSort ?? false;
  1110.     }
  1111.     public function isBrands(): bool
  1112.     {
  1113.         $aCurFilters $this->filterService()
  1114.             ->getCurFilters();
  1115.         if (count($aCurFilters) > 0) {
  1116.             foreach ($aCurFilters as $curFilter) {
  1117.                 if ($curFilter->isBrand()) {
  1118.                     return true;
  1119.                 }
  1120.             }
  1121.         }
  1122.         return false;
  1123.     }
  1124.     public function isDesigner(): bool
  1125.     {
  1126.         $aCurFilters $this->filterService()
  1127.             ->getCurFilters();
  1128.         if (count($aCurFilters) > 0) {
  1129.             foreach ($aCurFilters as $curFilter) {
  1130.                 if ($curFilter->isDesignerUser()) {
  1131.                     return true;
  1132.                 }
  1133.             }
  1134.         }
  1135.         return false;
  1136.     }
  1137.     public function showReview(): bool
  1138.     {
  1139.         // Вернули показ только при выборе коллекции
  1140.         // https://te.remote.team/#/discus/4353F149-3BFD-EC18-F321-C8300492082A/
  1141.         $aCurFilters $this->filterService()
  1142.             ->getCurFilters();
  1143.         if (count($aCurFilters) > 0) {
  1144.             foreach ($aCurFilters as $curFilter) {
  1145.                 if ($curFilter->isBrand()) {
  1146.                     return true;
  1147.                 }
  1148.             }
  1149.         }
  1150.         return false;
  1151.     }
  1152.     /**
  1153.      * Определяем запрос топа или нет по группе каталога
  1154.      *
  1155.      * @param null $altName
  1156.      *
  1157.      * @return bool|string
  1158.      * @throws Exception
  1159.      */
  1160.     private function isTop($altName null)
  1161.     {
  1162.         $aCurFilters $this->filterService()
  1163.             ->getCurFilters();
  1164.         if (count($aCurFilters) > 0) {
  1165.             foreach ($aCurFilters as $curFilter) {
  1166.                 if ($curFilter->isTop()) {
  1167.                     if (!$altName) {
  1168.                         return $curFilter->getAltName();
  1169.                     }
  1170.                     if ($curFilter->getAltName() == $altName) {
  1171.                         return $altName;
  1172.                     }
  1173.                 }
  1174.             }
  1175.         }
  1176.         return false;
  1177.     }
  1178.     private function getAlsoCollViewed(): array
  1179.     {
  1180.         $id $this->getSearchSubKey();
  1181.         return $this->collectionAlsoViewedRepository->getCollectionAlsoViewed($id);
  1182.     }
  1183.     /**
  1184.      * Получение топ за месяц
  1185.      */
  1186.     private function getSearchTopMonth(): ?array
  1187.     {
  1188.         return $this->interiorHistoryRepository->getTopCollections(301000);
  1189.     }
  1190.     /**
  1191.      * Получение топ за неделю
  1192.      */
  1193.     private function getSearchTopWeek(): ?array
  1194.     {
  1195.         return $this->interiorHistoryRepository->getTopCollections(71000);
  1196.     }
  1197.     /**
  1198.      * @return string
  1199.      */
  1200.     public function getGclid()
  1201.     {
  1202.         return $this->gclid;
  1203.     }
  1204.     /**
  1205.      * @param string $gclid
  1206.      */
  1207.     public function setGclid($gclid)
  1208.     {
  1209.         $this->gclid $gclid;
  1210.     }
  1211.     /**
  1212.      * Убираем из урл не верные символы и проводим его в нормальному виду
  1213.      *
  1214.      * @param $key
  1215.      *
  1216.      * @return mixed
  1217.      */
  1218.     private function normaliseKey($key)
  1219.     {
  1220.         $keyNorm trim($key);
  1221.         $keyNorm StrHelper::toLower($keyNorm);
  1222.         $keyNorm urldecode($keyNorm);
  1223.         ////////////////////////////////////////////////////////////////////
  1224.         $aBadKey = ['spal\'nya''tile_for_kids''non-slip''terakota-cotto-efekt'];
  1225.         $aGoodKey = ['spalnya''spaces-for-children''antislip''terracotta-effetto'];
  1226.         $keyNorm str_replace($aBadKey$aGoodKey$keyNorm);
  1227.         ////////////////////////////////////////////////////////////////////
  1228.         $aBadSimbol = [
  1229.             '%C3%BC',
  1230.             '%26',
  1231.             '_',
  1232.             'antypoślizgowość',
  1233.             ' ',
  1234.             '%20',
  1235.             'ã±',
  1236.             'ñ',
  1237.             'ã³',
  1238.             'å„',
  1239.             'å‚',
  1240.             'ã¨',
  1241.             'ã©',
  1242.             '%C5%82',
  1243.             'ł',
  1244.             '\'a=0'
  1245.         ];
  1246.         $aGoodSimbol = ['ü''&''-''antypoślizgowe''-''-''n''n''ó''ń''ł''è''é''l''l'''];
  1247.         $keyNorm str_replace($aBadSimbol$aGoodSimbol$keyNorm);
  1248.         // разбираем строку запроса и собираем ее нормальную снова
  1249.         $aKeyNorm explode($this->separator$keyNorm);
  1250.         $keyNorm '';
  1251.         foreach ($aKeyNorm as $item) {
  1252.             $item trim($item'-');
  1253.             $keyNorm .= $this->separator $item;
  1254.         }
  1255.         $keyNorm trim($keyNorm$this->separator);
  1256.         $keyNorm explode($this->separator$keyNorm);
  1257.         if (count($keyNorm) > 1) {
  1258.             $keyNormTmp = [];
  1259.             foreach ($keyNorm as $keyVal) {
  1260.                 if ($keyVal != 'coll') {
  1261.                     $keyNormTmp[] = trim($keyVal);
  1262.                 }
  1263.             }
  1264.             $keyNorm implode($this->separator$keyNormTmp);
  1265.         } else {
  1266.             $keyNorm implode($this->separator$keyNorm);
  1267.         }
  1268.         return $keyNorm;
  1269.     }
  1270.     /**
  1271.      * Формируем отображение слова collections для результата поиска со скланениями и учетом локали
  1272.      *
  1273.      * @param $settings
  1274.      * @param $count
  1275.      * @param null $locale
  1276.      *
  1277.      * @return string|Response
  1278.      * @throws Exception
  1279.      */
  1280.     public function setCountResultNew($settings$count$locale null)
  1281.     {
  1282.         $locale $locale ?: $this->localeVo->getCode();
  1283.         $strCollCount App::getTranslator()
  1284.             ->trans(
  1285.                 'catalog.settings.count_result',
  1286.                 [
  1287.                     '%count%' => App::plural($count),
  1288.                     '%cnt%' => $count,
  1289.                 ],
  1290.                 null,
  1291.                 $locale
  1292.             );
  1293.         $text $this->renderH1(
  1294.             [
  1295.                 'settings' => $settings,
  1296.                 'strCollCount' => $strCollCount,
  1297.                 'count' => $count,
  1298.             ],
  1299.             'header'
  1300.         );
  1301.         return $text;
  1302.     }
  1303.     /**
  1304.      * Формируем отображение слова collections для результата поиска со скланениями и учетом локали
  1305.      *
  1306.      * @param        $count
  1307.      * @param string|null $locale
  1308.      *
  1309.      * @return null
  1310.      * @throws Exception
  1311.      */
  1312.     public function setResultAmount($count, ?string $locale null)
  1313.     {
  1314.         if ($this->isTop('novelty')) {
  1315.             return null;
  1316.         }
  1317.         if ($this->isBrands() && $this->isOneFilter()) {
  1318.             return null;
  1319.         }
  1320.         $locale $locale ?: $this->localeVo->getCode();
  1321.         return App::getTranslator()
  1322.             ->trans(
  1323.                 'catalog.settings.count_result',
  1324.                 [
  1325.                     '%count%' => App::plural($count),
  1326.                     '%cnt%' => $count,
  1327.                 ],
  1328.                 null,
  1329.                 $locale
  1330.             );
  1331.     }
  1332.     /**
  1333.      * Проверяем наличие фильтра releaseYear на странице
  1334.      * @return bool
  1335.      */
  1336.     public function isPageReleaseYear()
  1337.     {
  1338.         $aCurFilters $this->getSearchFilter();
  1339.         return !empty($aCurFilters['releaseYear']);
  1340.     }
  1341.     /**
  1342.      * Проверяем наличие фильтра exhibition на странице
  1343.      * @return bool
  1344.      */
  1345.     public function isPageExhibition()
  1346.     {
  1347.         $aCurFilters $this->getSearchFilter();
  1348.         return !empty($aCurFilters['exhibition']);
  1349.     }
  1350.     /**
  1351.      * ПРоверка на одиночный фильтр быстрых образцов
  1352.      * @return bool
  1353.      * @throws Exception
  1354.      */
  1355.     public function isPageExpressSample(): bool
  1356.     {
  1357.         if (!$this->isOneFilter()) {
  1358.             return false;
  1359.         }
  1360.         $aCurFilters $this->getSearchFilter();
  1361.         return !empty($aCurFilters['expressSample']);
  1362.     }
  1363.     /**
  1364.      * Пишем лог по редиректам каталога
  1365.      *
  1366.      * @param $uriRedirect
  1367.      *
  1368.      * @throws Exception
  1369.      */
  1370.     private function writeLogRedirect($uriRedirect)
  1371.     {
  1372.         $idTheme '9CA6E81A-5702-F84B-94E5-27704B4CDF90';
  1373.         $idUser = ['145925377856FA72121B755145925377']; //mshirnin
  1374.         $unid null;
  1375.         $uriCat $this->generateUrl('app_catalog', ['_locale' => $this->localeVo->getCode()]);
  1376.         $oRequest RequestHelper::syRequest();
  1377.         $uriTo $oRequest->getRequestUri();
  1378.         // Проверяем, если редирект на главную каталога, то пишем в лог данные запроса.
  1379.         if ($oRequest->getRealMethod() != 'POST' and md5($uriCat) == md5($uriRedirect)) {
  1380.             $referer = !empty($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : null;
  1381.             // если есть реферер
  1382.             if ($referer) {
  1383.                 $urlParse parse_url($referer);
  1384.                 if ($oRequest->getHost() == "tile.expert") {
  1385.                     // пишем сначала на портал
  1386.                     //////////////////////////////////////////////////////////////
  1387.                     $portalHelper App::getPortal();
  1388.                     $subject "BadRedirect $uriTo";
  1389.                     $body "<p>Битая ссылка, требуется проверка запроса и исправление, если редирект не корректный, данные:</p>" "<ul>" "<li><b>Хост</b>: " App::getDomain(
  1390.                     ) . "</li>" "<li><b>Method</b>: {$oRequest->getRealMethod()}</li>" "<li><b>Откуда</b>: {$urlParse['path']}</li>" "<li><b>Куда</b>: $uriTo</li>" "</ul>";
  1391.                     $comm $portalHelper->checkExistComm($idTheme$subject);
  1392.                     if (!$comm) {
  1393.                         $task = [
  1394.                             'subjectID' => $idTheme,
  1395.                             'subject' => $subject,
  1396.                             'body' => $body,
  1397.                             'taskPerformerLat' => $idUser,
  1398.                         ];
  1399.                         $unid $portalHelper->createTask($task);
  1400.                         $unid = !empty($unid['unid']) ? $unid['unid'] : null;
  1401.                     }
  1402.                     // пишем в файл
  1403.                     // app.INFO: [redirect_catalog] urlCur: /en/catalogue/ABITAb, urlRef: , data: {"_controller":"WebBundle\\Controller\\CatalogController::indexAction","key":"ABITAb","subkey":"","_locale":"en","_route":"app_catalog","_route_params":{"key":"ABITAb","subkey":"","_locale":"en"}} [] []
  1404.                     // grep -ri  'redirect_catalog' /var/www/тут домен/app/logs/ >> redirect_catalog.txt
  1405.                     //////////////////////////////////////////////////////////////
  1406.                     $logger App::getContainer()
  1407.                         ->get('logger_public');
  1408.                     $requestData json_encode(RequestHelper::get());
  1409.                     $logger->info(
  1410.                         "[redirect_catalog] unid: $unid, uriTo: $uriTo, uriRef: $referer, data: $requestData"
  1411.                     );
  1412.                 }
  1413.             }
  1414.         }
  1415.     }
  1416.     /**
  1417.      * Ссылка на расширенный поиск, только если переход был с него, либо установлен флаг showBackToAdvanced
  1418.      */
  1419.     private function getLinkToAdvancedSearchIfNecessary(?string $titleHtml): ?string
  1420.     {
  1421.         $referer RequestHelper::syRequest()->headers->get('referer');
  1422.         $showBackToAdvanced RequestHelper::syRequest()->query->get('showBackToAdvanced');
  1423.         if (($referer && strpos($referer'advanced-search') !== false) || $showBackToAdvanced) {
  1424.             $link App::getRouter()
  1425.                 ->generate(
  1426.                     'app_filters_advanced_search',
  1427.                     [
  1428.                         'c' => $this->filterService()
  1429.                             ->getUrl()
  1430.                     ]
  1431.                 );
  1432.             $link urldecode($link);
  1433.             $link $this->render(
  1434.                 '@Web/Catalog/linkback-to-filterpage.html.twig',
  1435.                 ['link' => $link]
  1436.             );
  1437.             $titleHtml $link $titleHtml;
  1438.         }
  1439.         return $titleHtml;
  1440.     }
  1441. }