src/WebBundle/Service/SearchService.php line 550

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