src/WebBundle/Service/FiltersService.php line 1159

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\DTO\BrandResponseDTO;
  7. use FlexApp\DTO\FilterCatalogDTO;
  8. use FlexApp\DTO\FilterGroupCatalogDTO;
  9. use FlexApp\DTO\FilterGroupsCatalogDTO;
  10. use FlexApp\DTO\FilterResponseDTO;
  11. use FlexApp\Service\RedisCachePool;
  12. use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
  13. use WebBundle\Entity\Article;
  14. use WebBundle\Entity\Collection;
  15. use WebBundle\Helper\App;
  16. use WebBundle\Helper\ArrHelper;
  17. use WebBundle\Helper\ConversionHelper;
  18. use WebBundle\Helper\LocaleHelper;
  19. use WebBundle\Helper\RequestHelper;
  20. use WebBundle\Helper\StrHelper;
  21. use WebBundle\Repository\ArticleRepository;
  22. use WebBundle\Repository\CollectionRepository;
  23. use WebBundle\Repository\FilterRepository;
  24. use WebBundle\Repository\LastUrlRepository;
  25. class FiltersService extends ExtendService
  26. {
  27.     private string $locale;
  28.     private ?int $alsoCollViewedId null;
  29.     private ?bool $isOneFilter null;
  30.     /**
  31.      * Исходные выбранные ID фильтров ДО применения margeFilters().
  32.      * Нужны, чтобы отличать "родитель выбран, потому что выбрали всех детей"
  33.      * от ситуации "в данных пришёл только родитель" (когда нельзя автоматически
  34.      * подставлять/показывать всех детей).
  35.      *
  36.      * @var array<int|string>
  37.      */
  38.     private array $aCurFilterIdsOriginal = [];
  39.     private $urlKeyStr = [];
  40.     /** @var array|FilterResponseDTO[] */
  41.     private array $curFilters = [];
  42.     /** массив id фильтров */
  43.     protected $aCurFilterIds = [];
  44.     /** не найденные ключи из строки запроса*/
  45.     protected array $aBadKeys = [];
  46.     /** разделить параметров УРЛ фильтров  '&'*/
  47.     protected string $separator CatalogConst::SEPARATOR;
  48.     /** старый разделитель (для совместимости/301 редиректов) */
  49.     protected ?string $separatorOld null;
  50.     /** Массив для фильтра размеров */
  51.     private array $aDimensions = [];
  52.     /** Массив для фильтра БМов */
  53.     private array $aBMs = [];
  54.     /** @var CollectionRepository */
  55.     protected $collRepo;
  56.     /** @var FilterRepository */
  57.     protected $filterRepo;
  58.     /** @var ArticleRepository */
  59.     protected $articleRepo;
  60.     /**
  61.      * @throws Exception
  62.      */
  63.     public function __construct()
  64.     {
  65.         $this->locale App::getCurLocale();
  66.         // Разделитель ключей фильтров: берём из параметров (по умолчанию '&')
  67.         try {
  68.             $newSeparator App::getParameter('filters.separator.new');
  69.             if (is_string($newSeparator) && $newSeparator !== '') {
  70.                 $this->separator $newSeparator;
  71.             }
  72.         } catch (\Throwable $e) {
  73.             // noop: оставляем CatalogConst::SEPARATOR
  74.         }
  75.         try {
  76.             $oldSeparator App::getParameter('filters.separator.old');
  77.             if (is_string($oldSeparator) && $oldSeparator !== '') {
  78.                 $this->separatorOld $oldSeparator;
  79.             }
  80.         } catch (\Throwable $e) {
  81.             // noop
  82.         }
  83.         $this->collRepo App::getRepository('WebBundle:Collection');
  84.         $this->filterRepo App::getRepository('WebBundle:FilterEntity');
  85.         $this->articleRepo App::getRepository('WebBundle:Article');
  86.         return $this;
  87.     }
  88.     /**
  89.      * Сортировка , что бы вывод был в том положении, как лайт меню
  90.      * @return FilterResponseDTO[]
  91.      */
  92.     private function getSortedFiltersLikeInTheLiteMenu()
  93.     {
  94.         $pattern = [
  95.             'brand',
  96.             'effect',
  97.             'style',
  98.             'price',
  99.             'color',
  100.             'using',
  101.             'samples',
  102.         ];
  103.         $arr = [];
  104.         foreach ($this->curFilters as $filter) {
  105.             $arr[$filter->getGroupAltName()][] = $filter;
  106.         }
  107.         $res = [];
  108.         foreach (array_flip($pattern) as $k => $v) {
  109.             if (isset($arr[$k])) {
  110.                 $res[$k] = $arr[$k];
  111.                 unset($arr[$k]);
  112.             }
  113.         }
  114.         $af = [];
  115.         foreach ($res as $v) {
  116.             $af array_merge($af$v);
  117.         }
  118.         foreach ($arr as $v) {
  119.             $af array_merge($af$v);
  120.         }
  121.         return $af;
  122.     }
  123.     /**
  124.      * @param array $curFilters
  125.      */
  126.     private function setCurFilters(array $curFilters)
  127.     {
  128.         $this->curFilters $curFilters;
  129.     }
  130.     private function margeFilters()
  131.     {
  132.         // Сохраняем исходный набор выбранных фильтров до "сжатия" детей в родителя
  133.         if (!$this->aCurFilterIdsOriginal) {
  134.             $this->aCurFilterIdsOriginal $this->aCurFilterIds;
  135.         }
  136.         $parentMap $this->filterRepo->getFiltersSubRelation(true);
  137.         $result $this->aCurFilterIds;
  138.         foreach ($parentMap as $parentId => $childIds) {
  139.             // Проверяем, все ли дочерние ID присутствуют в выбранных фильтрах
  140.             $allChildrenPresent true;
  141.             foreach ($childIds as $childId) {
  142.                 if (!in_array($childId$result)) {
  143.                     $allChildrenPresent false;
  144.                     break;
  145.                 }
  146.             }
  147.             // Если все дети присутствуют, заменяем их на родителя
  148.             if ($allChildrenPresent) {
  149.                 $result array_diff($result$childIds); // Удаляем детей
  150.                 $result[] = $parentId// Добавляем родителя
  151.             }
  152.         }
  153.         $this->aCurFilterIds array_values($result);
  154.     }
  155.     public function isMargeFilters(int $filterId 0): bool
  156.     {
  157.         $parentMap $this->filterRepo->getFiltersSubRelation(true);
  158.         if (!array_key_exists($filterId$parentMap)) {
  159.             return false;
  160.         }
  161.         // ВАЖНО: проверяем по исходным выбранным ID (до margeFilters),
  162.         // чтобы "показывать всех детей" только когда реально выбрали ВСЕХ детей.
  163.         $original $this->aCurFilterIdsOriginal ?: $this->aCurFilterIds;
  164.         $originalStr array_map('strval'$original);
  165.         foreach ($parentMap[$filterId] as $childId) {
  166.             if (!in_array((string) $childId$originalStrtrue)) {
  167.                 return false;
  168.             }
  169.         }
  170.         return true;
  171.     }
  172.     public function unMargeFilters()
  173.     {
  174.         $parentMap $this->filterRepo->getFiltersSubRelation(true);
  175.         $results $this->aCurFilterIds;
  176.         foreach ($results as $result) {
  177.             // Проверяем, все ли дочерние ID присутствуют в выбранных фильтрах
  178.             $allChildrenPresent true;
  179.             if (array_key_exists($result$parentMap)) {
  180.                 $results array_merge($parentMap[$result], $results);
  181.                 //$results = array_diff($results, [$result]);
  182.             }
  183.         }
  184.         $this->aCurFilterIds array_values($results);
  185.         $filters $this->filterRepo->getSortedByIds($this->aCurFilterIds$this->locale);
  186.         $this->setCurFilters($filters);
  187.     }
  188.     /**
  189.      * Универсальная загрузка данных, написал, но еще не использовал
  190.      * @param null $locale
  191.      * @return $this
  192.      * @throws Exception
  193.      */
  194.     private function loadDataUniversal($locale null)
  195.     {
  196.         $locale = !$locale $this->locale $locale;
  197.         if (!$this->curFilters) {
  198.             if (RequestHelper::isAjax()) {
  199.                 $this->loadBySearchData(RequestHelper::get('data', []));
  200.             } else {
  201.                 $this->loadByUrlKey(App::getRequest()->get('key'), $locale);
  202.             }
  203.         }
  204.         return $this;
  205.     }
  206.     /**
  207.      * Поиск по ID фильтров
  208.      * @param $id
  209.      * @return $this
  210.      */
  211.     public function loadByIds($idstring $lc)
  212.     {
  213.         $id is_array($id) ? $id : [$id];
  214.         $this->aCurFilterIds = [];
  215.         $this->aCurFilterIdsOriginal = [];
  216.         $idsParam = [];
  217.         foreach ($id as $item) {
  218.             // обработка через :: для запросов по размерам, т.к. ID там идет вида 56564::0.5
  219.             // где первое - ID, а второе параметр
  220.             $ids explode('::'$item);
  221.             $this->aCurFilterIds[] = $ids[0];
  222.             if (count($ids) > 1) {
  223.                 $idsParam[$ids[0]] = $ids[1];
  224.             }
  225.         }
  226.         // фиксируем "как пришло" до сжатия
  227.         $this->aCurFilterIdsOriginal $this->aCurFilterIds;
  228.         $this->margeFilters();
  229.         $filters $this->filterRepo->getSortedByIds($this->aCurFilterIds$lc);
  230.         foreach ($filters as $i => $filter) {
  231.             // исключаем не активные фильры, если это не фабрики
  232.             if (!$filter->isEnable() and !$filter->getBrand()) {
  233.                 unset($filters[$i]);
  234.             }
  235.         }
  236.         if (count($idsParam) > 0) {
  237.             foreach ($filters as $filter) {
  238.                 if ($filter->isDimension()) {
  239.                     $this->aDimensions[$filter->getAltName()] = $idsParam[$filter->getId()];
  240.                 }
  241.                 if ($filter->isBM()) {
  242.                     $this->aBMs[$filter->getAltName()] = $idsParam[$filter->getId()];
  243.                 }
  244.             }
  245.         }
  246.         $this->setCurFilters($filters);
  247.         return $this;
  248.     }
  249.     public function loadBySearchData($data)
  250.     {
  251.         // новая загрузка — сбрасываем "исходные" выбранные ID до merge
  252.         $this->aCurFilterIdsOriginal = [];
  253.         if (count($data) > 0) {
  254.             $keys $this->findByBaseInOldCommand($data);
  255.             /**
  256.              * Получаем ID сортировки из запроса
  257.              */
  258.             $sortId RequestHelper::get('sort');
  259.             if (!$sortId) {
  260.                 $sortId ArrHelper::get($this->getSearchFilter(), 'getSort.0');
  261.                 $cookies App::getRequest()->cookies;
  262.                 // если нашли ID сортировки, то пишем его в куки
  263.                 if ($sortId) {
  264.                     $cookies->set('sort_catalog'$sortId);
  265.                 }
  266.                 // получаем итоговую сортировку
  267.                 $sortId $cookies->get('sort_catalog');
  268.                 // если не определна. то ставим поппулярность по умолчанию.
  269.                 if (!$sortId) {
  270.                     $cookies->set('sort_catalog'1);
  271.                 }
  272.             }
  273.             $sStyleDesigner $this->isStyleDesignerAndDesigner($keys);
  274.             // пишем ID фильров в массив, из которого и будем формировать всякие URL и прочее
  275.             foreach ($keys as $filter) {
  276.                 if ($filter->isEnable()) {
  277.                     // выкидываем из фильтров сочетание дизайнера + дизайнерский стиль
  278.                     if ($sStyleDesigner) {
  279.                         if ($filter->isDesignerStyle()) {
  280.                             continue;
  281.                         }
  282.                     }
  283.                     if ($filter->isSort()) {
  284.                         continue;
  285.                     }
  286.                     $this->aCurFilterIds[] = $filter->getId();
  287.                     // проверяем на наличие группы фильтров с размерами, если нашли, то
  288.                     // получаем KEY фильтра в текущей локали и по нему ищем значение с троке
  289.                     // после чего формируем массив вида ["size_x" => "40"]
  290.                     if ($filter->isDimension()) {
  291.                         $alias $filter->getAltName();
  292.                         foreach ($data as $iAlias => $aVal) {
  293.                             if ($iAlias == $alias) {
  294.                                 $this->aDimensions[$alias] = $aVal[0];
  295.                             }
  296.                         }
  297.                     }
  298.                     if ($filter->isBM()) {
  299.                         $alias $filter->getAltName();
  300.                         foreach ($data as $iAlias => $aVal) {
  301.                             if ($iAlias == $alias) {
  302.                                 $this->aBMs[$alias] = $aVal[0];
  303.                             }
  304.                         }
  305.                     }
  306.                 }
  307.             }
  308.             // делаем еще запрос в базу, что бы получить объекты фильтров в нужной сортировке
  309.             $this->margeFilters();
  310.             $this->setCurFilters($this->filterRepo->getSortedByIds($this->aCurFilterIds$this->locale));
  311.         }
  312.         return $this;
  313.     }
  314.     //** ресетируем сервис для генерации правильных урл для sitemap и googleAds */
  315.     public function reset(): void
  316.     {
  317.         $this->curFilters = [];
  318.         $this->urlKeyStr = [];
  319.         $this->aCurFilterIds = [];
  320.         $this->aCurFilterIdsOriginal = [];
  321.         $this->searchFilter = [];
  322.     }
  323.     /**
  324.      * Инициируем сам класс, по разбору строки запроса
  325.      * с поиском данных в базе фильтров
  326.      * @param string|null $key
  327.      * @param null $locale
  328.      * @return $this
  329.      * @throws Exception
  330.      */
  331.     public function loadByUrlKey($key$locale null)
  332.     {
  333.         // новая загрузка — сбрасываем "исходные" выбранные ID до merge
  334.         $this->aCurFilterIdsOriginal = [];
  335.         if ($key) {
  336.             $keys $this->urlToArray($key);
  337.             $cntDesigner 0;
  338.             if (count($keys) > 0) {
  339.                 $filters $this->findByBase($keys$locale);
  340.                 // если фильтр AlsoCollViewed , то остальные убираем, на вариант с совпадением по ID
  341.                 if (!empty($filters[10504])) {
  342.                     $filters = [$filters[10504]];
  343.                     $this->alsoCollViewedId $keys[1];
  344.                 }
  345.                 /**
  346.                  * пишем ID фильров в массив, из которого и будем формировать всякие URL и прочее
  347.                  */
  348.                 foreach ($filters as $filter) {
  349.                     // исключаем не активные фильры, если это не фабрики
  350.                     if (!$filter->isEnable() and !$filter->isBrand()) {
  351.                         continue;
  352.                     }
  353.                     // фабрики проверяем дополнительно
  354.                     if ($oBrand $filter->getBrand()) {
  355.                         if (!$oBrand->isShowPage()) {
  356.                             continue;
  357.                         }
  358.                     }
  359.                     // для дизайнеров ограничиваем вывод - не более одного фильтруется
  360.                     if ($filter->isDesignerUser()) {
  361.                         if ($cntDesigner !== 0) {
  362.                             continue;
  363.                         }
  364.                         $cntDesigner++;
  365.                     }
  366.                     // если в запросе сортировка, то пишем данные в куки, а сам фильтр исключаем
  367.                     if ($filter->isSort()) {
  368.                         $cookies App::getRequest()->cookies;
  369.                         if (RequestHelper::get('sort-tmp')) {
  370.                             // временное изменение сортировки, для ссылки New Arrivals с главной сайта
  371.                             App::getSession()->set('sort_catalog_tmp'$filter->getSphinxId());
  372.                         } else {
  373.                             $cookies->set('sort_catalog'$filter->getSphinxId());
  374.                         }
  375.                         continue;
  376.                     }
  377.                     $this->aCurFilterIds[] = $filter->getId();
  378.                     $this->aCurFilterIds array_unique($this->aCurFilterIds);
  379.                     // проверяем на наличие группы фильтров с размерами, если нашли, то
  380.                     // получаем KEY фильтра в текущей локали и по нему ищем значение с троке
  381.                     // после чего формируем массив вида ["size_x" => "40"]
  382.                     if ($filter->isDimension()) {
  383.                         foreach ($keys as $iKey) {
  384.                             $keyDimensionsAll $filter->getSlugsLc();
  385.                             foreach ($keyDimensionsAll as $keyDim) {
  386.                                 if (strval($keyDim) && strpos($iKeystrval($keyDim)) !== false) {
  387.                                     $size str_replace($keyDim '-'''$iKey);
  388.                                     $this->aDimensions[$filter->getAltName()] = $size;
  389.                                 }
  390.                             }
  391.                         }
  392.                     }
  393.                     if ($filter->isBM()) {
  394.                         foreach ($keys as $iKey) {
  395.                             $keyBMs $filter->getSlug();
  396.                             if (strval($keyBMs) && strpos($iKeystrval($keyBMs)) !== false) {
  397.                                 $this->aBMs[$filter->getAltName()] = str_replace($keyBMs '-'''$iKey);
  398.                             }
  399.                         }
  400.                     }
  401.                 }
  402.                 // делаем еще запрос в базу, что бы получить объекты фильтров в нужной сортировке
  403.                 if ($this->aCurFilterIds) {
  404.                     $this->margeFilters();
  405.                     $filters $this->filterRepo->getSortedByIds($this->aCurFilterIds$locale);
  406.                     $this->setCurFilters($filters);
  407.                 }
  408.                 if (!empty($filters) && $this->getSortFilter() == null) {
  409.                     //если фильтры есть исключаем береберду в запросах и ишют по цене или по звездам, то по умолчанию отдаем по поиску
  410.                     if (array_key_exists('getCostCategory'$this->getSearchFilter())) {
  411.                         App::getSession()->set('sort_catalog_tmp'2);
  412.                     } elseif (array_key_exists('reviews'$this->getSearchFilter())) {
  413.                         App::getSession()->set('sort_catalog_tmp'6);
  414.                     } elseif (array_key_exists('getTopMonth'$this->getSearchFilter())) {
  415.                         App::getSession()
  416.                             ->set('sort_catalog_tmp'5);
  417.                     } elseif (array_key_exists('getTopWeek'$this->getSearchFilter())) {
  418.                         App::getSession()
  419.                             ->set('sort_catalog_tmp'1);
  420.                     }
  421.                 }
  422.             }
  423.         }
  424.         return $this;
  425.     }
  426.     /**
  427.      * @param $altName
  428.      * @param string $locale
  429.      * @return $this
  430.      * @throws Exception
  431.      */
  432.     public function loadByAltName($altNamestring $locale)
  433.     {
  434.         $altName is_array($altName) ? $altName : [$altName];
  435.         $filters $this->filterRepo->getByAltNames($altName);
  436.         $fids = [];
  437.         foreach ($filters as $filter) {
  438.             $fids[] = $filter->getId();
  439.         }
  440.         $this->loadByIds($fids$locale);
  441.         return $this;
  442.     }
  443.     /**
  444.      * @param null $papam
  445.      * @param null $locale
  446.      * @return mixed
  447.      * @throws Exception
  448.      */
  449.     public function getUrl($papam null$locale null)
  450.     {
  451.         $word $this->getWord($papam$locale);
  452.         return $this->getFullUrlKeyStr($word$locale);
  453.     }
  454.     /**
  455.      * Получение KEY для URL строки
  456.      * @param null $papam
  457.      * @param null $locale
  458.      * @return string
  459.      * @throws Exception
  460.      */
  461.     public function getWord($papam null$locale null)
  462.     {
  463.         $locale = !$locale $this->locale $locale;
  464.         if (!$this->curFilters) {
  465.             if (!$papam) {
  466.                 // если ничего нет, то грузим то, что есть сейчас в зависимости от типа запроса
  467.                 $this->loadDataUniversal($locale);
  468.             } else {
  469.                 if (is_array($papam)) {
  470.                     if (!empty($papam[0])) {
  471.                         // массив индексный
  472.                         if (intval($papam[0]) === $papam[0]) {
  473.                             // ищем по ID
  474.                             $this->loadByIds($papam$locale);
  475.                         } else {
  476.                             // ищем по altName
  477.                             $this->loadByAltName($papam$locale);
  478.                         }
  479.                     } else {
  480.                         // массив именованый, значит имеем вид запроса ['getUsings'=>[10,12,58]]
  481.                         $this->loadBySearchData($papam);
  482.                     }
  483.                 } else {
  484.                     // если значение строковое, то обрабатываем отдельно
  485.                     if (strripos($papam$this->separator) !== false) {
  486.                         // ищем по KEY url
  487.                         $this->loadByUrlKey($papam$locale);
  488.                     } else {
  489.                         if (intval($papam) === $papam) {
  490.                             // ищем по ID
  491.                             $this->loadByIds($papam$locale);
  492.                         } else {
  493.                             // ищем по KEY url
  494.                             $this->loadByUrlKey($papam$locale);
  495.                             if (count($this->curFilters) < 1) {
  496.                                 // ищем по altName
  497.                                 $this->loadByAltName($papam$locale);
  498.                             }
  499.                         }
  500.                     }
  501.                 }
  502.             }
  503.         }
  504.         return $this->getUrlKeyStr($locale);
  505.     }
  506.     /**
  507.      * Получаем полную строку вместе с KEY по загруженным ранее данным
  508.      * @param null $key
  509.      * @param null $locale
  510.      * @return mixed
  511.      */
  512.     public function getFullUrlKeyStr($key null$locale null)
  513.     {
  514.         if (!$key) {
  515.             $key $this->getUrlKeyStr($locale);
  516.         }
  517.         // пока убрал, т.к. через него лезут левые запросы, нам не нужные
  518.         //      $subkey = RequestHelper::syRequest()->get('subkey');
  519.         //      if ($subkey) {
  520.         //          $params['subkey'] = $subkey;
  521.         //      }
  522.         $params['key'] = $key;
  523.         if ($locale) {
  524.             // если локаль соответсвует текущей, то проверяем на страну доставки
  525.             if ($locale == $this->locale) {
  526.                 $locale App::getCurLocale(true);
  527.             }
  528.             $params['_locale'] = $locale;
  529.         }
  530.         // Канонический URL для фильтров (без обязательного /subkey)
  531.         return str_replace('%26''&'$this->generateUrl('app_catalog_short'$params));
  532.     }
  533.     /**
  534.      * Получаем строку KEY по загруженным ранее данным
  535.      * Получение происходит по ID фильтров из массива
  536.      * @param null $locale
  537.      * @return string
  538.      */
  539.     public function getUrlKeyStr($locale null)
  540.     {
  541.         $locale = ($locale != null) ? $locale $this->locale;
  542.         if (empty($this->urlKeyStr[$locale])) {
  543.             $str '';
  544.             $isStyleDesignerClear $this->isStyleDesignerAndDesigner();
  545.             foreach ($this->curFilters as $filter) {
  546.                 // фильтр галвной каталога игнорируем при генерации URL
  547.                 if (!$filter->isRootCataloge()) {
  548.                     $slug $filter->getSlug($locale);
  549.                     if ($filter->isDimension()) {
  550.                         $altName $filter->getAltName();
  551.                         $size = (float)$this->aDimensions[$altName];
  552.                         $slug $slug '-' $size;
  553.                     }
  554.                     if ($filter->isBM()) {
  555.                         $altName $filter->getAltName();
  556.                         $slug $slug '-' $this->aBMs[$altName];
  557.                     }
  558.                     // урл сортировки не выводим
  559.                     if ($filter->isSort()) {
  560.                         $slug '';
  561.                     }
  562.                     if ($isStyleDesignerClear and $filter->isDesignerStyle()) {
  563.                         $slug '';
  564.                     }
  565.                     $str .= $slug $this->separator;
  566.                 }
  567.             }
  568.             $this->urlKeyStr[$locale] = trim($str$this->separator);
  569.         }
  570.         return $this->urlKeyStr[$locale];
  571.     }
  572.     //{"searchFilter":{"getFacturas":["5"]},"searchSort":"2","searchPeriod":null,"locale":"en"}
  573.     public function getSearchFilter()
  574.     {
  575.         $aSearchFilter = [];
  576.         foreach ($this->curFilters as $filter) {
  577.             $alias $filter->getSphinxName();
  578.             $id false;
  579.             if ($filter->isDimension()) {
  580.                 // если фильтр размеров, то ставим значение поиска, вместо ID фильтра
  581.                 // для строки &width-from-12000 это будет 1200
  582.                 $val = (float)$this->aDimensions[$alias];
  583.             } elseif ($filter->isBM()) {
  584.                 // если фильтр bm, то ставим значение поиска, вместо ID фильтра
  585.                 // для строки &bm-123123 это будет 123123
  586.                 $val $this->aBMs[$alias];
  587.             } elseif ($filter->isTop()) {
  588.                 // если фильтр top, то ставим значение subkey, вместо ID фильтра
  589.                 $val RequestHelper::get('subkey');
  590.                 //  $val = $filter->getId();
  591.             } elseif ($filter->isDesignerUser()) {
  592.                 // пока для дизайнеров делаем параметром их имя
  593.                 $val $filter->getNameSingle();
  594.                 $id $filter->getId();
  595.             } else {
  596.                 $val $filter->getSphinxId();
  597.             }
  598.             if ($id != false) {
  599.                 $aSearchFilter[$alias][] = "{$id}";
  600.             }
  601.             $aSearchFilter[$alias][] = "{$val}";
  602.         }
  603.         return (count($aSearchFilter) > 0) ? $aSearchFilter null;
  604.     }
  605.     /**
  606.      * @param array $data
  607.      * @return array|FilterResponseDTO[]
  608.      * @throws \Doctrine\DBAL\Driver\Exception
  609.      * @throws \Doctrine\DBAL\Exception
  610.      */
  611.     private function findByBaseInOldCommand(array $data)
  612.     {
  613.         $aKeysNew = [];
  614.         if (count($data) > 0) {
  615.             $filterRepo $this->filterRepo;
  616.             // первый проход поиска, ищем по колному кею
  617.             foreach ($data as $old_command => $ids) {
  618.                 // исключаем запросы с коллекциями
  619.                 if ($old_command == 'collection') {
  620.                     continue;
  621.                 }
  622.                 if ($old_command == 'factory') {
  623.                     if (count($ids) > 1) {
  624.                         unset($ids[1]);
  625.                     }
  626.                 } elseif ($old_command == 'getAlsoCollViewed') {
  627.                     $ids = [0];
  628.                 }
  629.                 if ($old_command == 'collection') {
  630.                     continue;
  631.                 }
  632.                 if ($old_command == 'getDesigner') {
  633.                     $filters $filterRepo->getSortedByIds($ids$this->locale);
  634.                 } else {
  635.                     if (!is_array($ids)) {
  636.                         $ids = [$ids];
  637.                     }
  638.                     $filters $filterRepo->getByOldCommand($ids$old_command$this->locale);
  639.                 }
  640.                 if ($filters) {
  641.                     unset($data[$old_command]);
  642.                     foreach ($filters as $filter) {
  643.                         $aKeysNew[$filter->getId()] = $filter;
  644.                     }
  645.                 } else {
  646.                     $filters $filterRepo->getFilterDTOByAltName($old_command$this->locale);
  647.                     if (count($filters) == 1) {
  648.                         $filter $filters[0];
  649.                         unset($data[$old_command]);
  650.                         $aKeysNew[$filter->getId()] = $filter;
  651.                     }
  652.                 }
  653.             }
  654.         }
  655.         // если в массиве еще остались значения , то пишем значения в массив в бед кеями
  656.         // что с ним потом делать пока не знаю, но что то делать будет надо
  657.         if (count($data) > 0) {
  658.             foreach ($data as $key) {
  659.                 $this->aBadKeys[] = $key;
  660.             }
  661.         }
  662.         return $aKeysNew;
  663.     }
  664.     /**
  665.      * Ищем значения в базе по имени фильтра
  666.      * @param array $aKeys
  667.      * @param string|null $locale
  668.      * @return array|FilterResponseDTO[]
  669.      * @throws \Doctrine\DBAL\Driver\Exception
  670.      * @throws \Doctrine\DBAL\Exception
  671.      */
  672.     private function findByBase(array $aKeys$locale null)
  673.     {
  674.         $aKeysNew = [];
  675.         $repoFilter $this->filterRepo;
  676.         if (count($aKeys) > 0) {
  677.             $isSort false;
  678.             // первый проход поиска, ищем по полному кею
  679.             foreach ($aKeys as $i => $key) {
  680.                 // доп логика для фильтрации БМов
  681.                 if (preg_match("/^bm-(\d*)$/"$key)) {
  682.                     $key 'bm';
  683.                 }
  684.                 $filter $repoFilter->getByKeyUrl($key$locale);
  685.                 // если фильтр не нашли, то проходимся дополнительно разложив кей на части
  686.                 if (!$filter) {
  687.                     $dopKeys $this->buldExplodeKeys($key);
  688.                     foreach ($dopKeys as $dkey) {
  689.                         $filter $repoFilter->getByKeyUrl($dkey$locale);
  690.                         if ($filter) {
  691.                             break;
  692.                         }
  693.                     }
  694.                     if (!$filter) {
  695.                         foreach ($dopKeys as $dkey) {
  696.                             $filter $repoFilter->getByKeyUrlIsLike($dkey$locale);
  697.                             if ($filter) {
  698.                                 break;
  699.                             }
  700.                         }
  701.                     }
  702.                 }
  703.                 if ($filter) {
  704.                     // проверка на включенный фильтр
  705.                     // фильтр может быть отключен, если это фабрика. Фабрику выводим в любом случае
  706.                     if ($filter->isEnable() or $filter->isBrand()) {
  707.                         // проверяем, что бы сортировка была только одна
  708.                         if ($filter->isSort() && !$isSort) {
  709.                             $isSort true;
  710.                         }
  711.                         unset($aKeys[$i]);
  712.                         $aKeysNew[$filter->getId()] = $filter;
  713.                     }
  714.                 }
  715.             }
  716.             // если в массиве еще остались значения , то дополнительно проверяем на наличие редиректов
  717.             if (count($aKeys) > 0) {
  718.                 /** @var $repoLastUrl LastUrlRepository */
  719.                 $repoLastUrl App::getRepository('WebBundle:LastUrlEntity');
  720.                 foreach ($aKeys as $i => $key) {
  721.                     if ($keyTmp $repoLastUrl->getActualSlugFiflter($key$locale)) {
  722.                         if ($filter $this->filterRepo->getByKeyUrl($keyTmp$locale)) {
  723.                             unset($aKeys[$i]);
  724.                             $aKeysNew[$filter->getId()] = $filter;
  725.                         }
  726.                     }
  727.                 }
  728.             }
  729.         }
  730.         // если в массиве еще остались значения , то пишем значения в массив в бед кеями
  731.         // что с ним потом делать пока не знаю, но что то делать будет надо
  732.         if (count($aKeys) > 0) {
  733.             foreach ($aKeys as $key) {
  734.                 $this->aBadKeys[] = $key;
  735.             }
  736.         }
  737.         return $aKeysNew;
  738.     }
  739.     /**
  740.      * @return array|FilterResponseDTO[]
  741.      */
  742.     public function getCurFilters()
  743.     {
  744.         return $this->curFilters;
  745.     }
  746.     /**
  747.      * @return array
  748.      */
  749.     public function getDimensions()
  750.     {
  751.         return $this->aDimensions;
  752.     }
  753.     /**
  754.      * @param mixed $aDimensions
  755.      */
  756.     public function setDimensions($aDimensions)
  757.     {
  758.         $this->aDimensions $aDimensions;
  759.     }
  760.     /**
  761.      * @return array
  762.      */
  763.     public function getBMs()
  764.     {
  765.         return $this->aBMs;
  766.     }
  767.     /**
  768.      * @param mixed $aBMs
  769.      */
  770.     public function setBMs($aBMs)
  771.     {
  772.         $this->aBMs $aBMs;
  773.     }
  774.     /**
  775.      * Формируем многомерный массив из строки запроса с фильтрами
  776.      * @param $key
  777.      * @return array
  778.      */
  779.     private function urlToArray($key)
  780.     {
  781.         $key StrHelper::toLower($key);
  782.         $key trim(urldecode($key));
  783.         // Совместимость: старые URL с '&' преобразуем в новый разделитель (например '--')
  784.         if ($this->separatorOld && $this->separatorOld !== $this->separator) {
  785.             $key str_replace($this->separatorOld$this->separator$key);
  786.         }
  787.         $key $key explode($this->separator$key) : [];
  788.         return array_diff($key, [''' 'nullfalse]);
  789.     }
  790.     /**
  791.      * Получаем объект фабрики, если таковая есть в фильтрах
  792.      * @return null|BrandResponseDTO
  793.      */
  794.     public function getCurBrand()
  795.     {
  796.         foreach ($this->getCurFilters() as $curFilter) {
  797.             if ($brand $curFilter->getBrand()) {
  798.                 return $brand;
  799.             }
  800.         }
  801.         return null;
  802.     }
  803.     /**
  804.      * Определяем сколько фильтров примененно, без учета сортировки
  805.      * @return bool
  806.      */
  807.     public function isOneFilter()
  808.     {
  809.         if ($this->isOneFilter === null) {
  810.             $aCurFilters $this->getCurFilters();
  811.             $countFilters count($aCurFilters);
  812.             if ($countFilters 1) {
  813.                 foreach ($aCurFilters as $filter) {
  814.                     if ($filter->isSort()) {
  815.                         $countFilters--;
  816.                     } else {
  817.                         if (!$filter->isEnable()) {
  818.                             $countFilters--;
  819.                         }
  820.                     }
  821.                 }
  822.             }
  823.             $this->isOneFilter = ($countFilters == 1);
  824.         }
  825.         return $this->isOneFilter;
  826.     }
  827.     /**
  828.      * Проверка на тестовую фабрику
  829.      * @return bool
  830.      */
  831.     public function isTestingBrand()
  832.     {
  833.         foreach ($this->curFilters as $filter) {
  834.             if ($filter->getAltName() == 'testing_factory') {
  835.                 return true;
  836.             }
  837.         }
  838.         return false;
  839.     }
  840.     /**
  841.      * Проверка на принадлежность к топу
  842.      * @return bool
  843.      */
  844.     public function isTopFilter()
  845.     {
  846.         foreach ($this->curFilters as $filter) {
  847.             if ($filter->isTop() && !$filter->isAlsoCollViewed()) {
  848.                 return true;
  849.             }
  850.         }
  851.         return false;
  852.     }
  853.     /**
  854.      * Проверка на сочетание двых выбранных фильтров дизайнерский стиль + любой дизайнер
  855.      * @param array $filters
  856.      * @return bool
  857.      */
  858.     public function isStyleDesignerAndDesigner(array $filters = [])
  859.     {
  860.         $filters $filters ?: $this->curFilters;
  861.         if (count($filters) == 2) {
  862.             $isDesigner $this->isExistDesigner($filters);
  863.             if ($isDesigner) {
  864.                 foreach ($filters as $filter) {
  865.                     if ($filter->isDesignerStyle()) {
  866.                         return true;
  867.                     }
  868.                 }
  869.             }
  870.         }
  871.         return false;
  872.     }
  873.     public function isExistDesigner(array $filters = []): bool
  874.     {
  875.         $filters $filters ?: $this->curFilters;
  876.         foreach ($filters as $filter) {
  877.             if ($filter->isDesignerUser()) {
  878.                 return true;
  879.             }
  880.         }
  881.         return false;
  882.     }
  883.     /**
  884.      * Получаем вильтр сортировки, если таковой был в запросе
  885.      * @return null|FilterResponseDTO
  886.      */
  887.     public function getSortFilter()
  888.     {
  889.         foreach ($this->getCurFilters() as $curFilter) {
  890.             if ($curFilter->isSort()) {
  891.                 return $curFilter;
  892.             }
  893.         }
  894.         return null;
  895.     }
  896.     /**
  897.      * @param FilterGroupsCatalogDTO $filterGroups
  898.      * @param bool $isShot
  899.      * @param bool $isLinkToFilter
  900.      * @param bool $isLinkEdit
  901.      * @return string
  902.      */
  903.     public function buldFiltersToStr(
  904.         FilterGroupsCatalogDTO $filterGroups,
  905.         bool $isShot false,
  906.         bool $isLinkToFilter false,
  907.         bool $isLinkEdit true
  908.     ) {
  909.         if ($this->alsoCollViewedId) {
  910.             return $filterGroups->getFirstGroup()->getFirstFilter()->getTitle();
  911.         }
  912.         $and $this->translate('joinder_and'$this->locale);
  913.         $ceramicTilesName $this->translate('catalog_ceramic_tiles'$this->locale);
  914.         $sTtitle2 "$ceramicTilesName ";
  915.         $isShowLinkStyleDesigner $isLinkToFilter;
  916.         if ($this->isOneFilter()) {
  917.             $sTtitle2 '';
  918.             $isLinkToFilter false;
  919.             $isShowLinkStyleDesigner $this->isExistDesigner();
  920.         }
  921.         if ($this->isOneFilter() && $this->getCurBrand()) {
  922.             $isLinkEdit false;
  923.         }
  924.         // type (вид изделия)
  925.         if ($typeGroup $filterGroups->getProductType()) {
  926.             $sTtitle2 $this->modifyFirstFilter($typeGroup);
  927.         } else {
  928.             // material
  929.             if ($materialGroup $filterGroups->getMaterial()) {
  930.                 $sTtitle2 $this->modifyFirstFilter($materialGroup);
  931.             }
  932.         }
  933.         if ($isShot) {
  934.             $sTtitle2 '';
  935.         }
  936.         $groups $filterGroups->getGroups();
  937.         $maxCntFilters 0;
  938.         foreach ($groups as $group) {
  939.             $cnt count($group->getListFilters());
  940.             if ($cnt $maxCntFilters) {
  941.                 $maxCntFilters $cnt;
  942.             }
  943.         }
  944.         if ($maxCntFilters 2) {
  945.             $separatorGroup ';';
  946.             $separatorFilter ',';
  947.         } else {
  948.             $separatorGroup ',';
  949.             $separatorFilter $and;
  950.         }
  951.         $cntGroup count($groups);
  952.         $isDotBefore false;
  953.         foreach ($groups as $i => $group) {
  954.             $groupName $group->getGroupName();
  955.             $altName $group->getGroupAltName();
  956.             if ($i == || $isDotBefore) {
  957.                 $groupName StrHelper::ucFirstOnly($groupName);
  958.             } else {
  959.                 $groupName StrHelper::toLower($groupName);
  960.             }
  961.             if ($altName == 'dimension' || $altName == 'rewards' || $altName == 'samples') {
  962.                 $groupName '';
  963.             } elseif ($altName == 'bm') {
  964.                 $groupName StrHelper::toUpper($groupName) . ': ';
  965.             } elseif ($altName == 'resistance_abrasion') {
  966.                 $groupName '';//StrHelper::toUpper($groupName);
  967.             }
  968.             if ($this->isOneFilter()) {
  969.                 $groupName '';
  970.             }
  971.             $groupName $groupName "{$groupName} " "";
  972.             $name_ '';
  973.             $listFilters $group->getListFilters();
  974.             $nameCnt count($listFilters);
  975.             foreach ($listFilters as $ii => $filter) {
  976.                 $pref '';
  977.                 if ($ii 0) {
  978.                     $pref $separatorFilter "{$separatorFilter} " ' ';
  979.                     // для последнего ставим
  980.                     if ($nameCnt == $ii) {
  981.                         $pref {$and} ";
  982.                     }
  983.                     // для размеров убираем разделители между двумя одинаковами типами
  984.                     if ($altName == 'dimension') {
  985.                         if (!empty($listFilters[$ii 1])) {
  986.                             $keyPrew $listFilters[$ii 1]->getKey();
  987.                             if ($filter->getKey() == $keyPrew '2') {
  988.                                 $pref ' ';
  989.                             }
  990.                         }
  991.                     }
  992.                 }
  993.                 $elFilterName $filter->getTitle() ?? '';
  994.                 if (!$groupName && $altName != 'resistance_abrasion') {
  995.                     if (($i == && $ii == 0) || $isDotBefore) {
  996.                         $elFilterName StrHelper::ucFirstOnly($elFilterName);
  997.                     } else {
  998.                         $elFilterName StrHelper::toLower($elFilterName);
  999.                     }
  1000.                 }
  1001.                 $isDotBefore preg_match('/\.\s?<?/'strip_tags($elFilterName));
  1002.                 if ($isLinkToFilter) {
  1003.                     $elFilterName $this->buildElemetFilter($elFilterName$filter->getSlug());
  1004.                 } elseif ($isShowLinkStyleDesigner) {
  1005.                     $key $this->filterRepo->getKeyDesignerStyle();
  1006.                     $link $this->generateUrl('app_catalog', ['key' => $key]);
  1007.                     $pref $this->translate('link_on_designer'null, ['%href%' => $link]) . ' ';
  1008.                     $elFilterName $filter->getNameMany();
  1009.                 }
  1010.                 if ($isLinkEdit) {
  1011.                     $elFilterName $this->addElemetFilterLinkEdit($filter->getId(), $filter->getKey(), $elFilterName);
  1012.                 }
  1013.                 // может пригодится если вернут -- Просьба убрать виды камня и мрамора из названия фильтра https://te2.remote.team/discus/88842E08-8611-0D89-5038-7CF7CADD7AF0?goto=true
  1014. //                if ($this->isMargeFilters($filter->getId())) {
  1015. //                    $sub = $this->filterRepo->getArrListSubForMenu($filter->getId(), $this->locale);
  1016. //
  1017. //                    $all_sub = [];
  1018. //                    foreach ($sub as $subItem) {
  1019. //                        if (!empty($subItem['pageNameMenu']) && !empty($subItem['url'])) {
  1020. //                            $all_sub[] = $this->buildElemetFilter($subItem['pageNameMenu'], $subItem['url']);
  1021. //                        }
  1022. //                    }
  1023. //                    if ($isLinkToFilter) {
  1024. //                        $elFilterName .= ' (' . implode(', ', $all_sub) . ')';
  1025. //                    } else {
  1026. //                        $elFilterName .= ': ' . implode(', ', $all_sub);
  1027. //                    }
  1028. //                }
  1029.                 $name_ .= "{$pref}{$elFilterName}";
  1030.             }
  1031.             // фикс случайных нескольких пробелов на всякий случай
  1032.             $name_ preg_replace(['# {2,}#umi'], ' '$name_);
  1033.             if ($i == $cntGroup 1) {
  1034.                 $sepGroup '';
  1035.             } else {
  1036.                 $sepGroup "{$separatorGroup} ";
  1037.             }
  1038.             // если нашли вконце точку, то разделитель убираем
  1039.             if ($isDotBefore) {
  1040.                 $sepGroup '';
  1041.             }
  1042.             if ($isLinkToFilter) {
  1043.                 $sTtitle2 .= "<span>{$groupName}{$name_}{$sepGroup}</span>";
  1044.             } else {
  1045.                 $sTtitle2 .= "{$groupName}{$name_}{$sepGroup}";
  1046.             }
  1047.         }
  1048.         return $sTtitle2;
  1049.     }
  1050.     /**
  1051.      * @param FilterGroupsCatalogDTO $filterGroups
  1052.      * @return string
  1053.      */
  1054.     public function buldFiltersToStrMetaTitle(
  1055.         FilterGroupsCatalogDTO $filterGroups
  1056.     ) {
  1057.         if ($this->alsoCollViewedId) {
  1058.             return $filterGroups->getFirstGroup()->getFirstFilter()->getTitle();
  1059.         }
  1060.         $and $this->translate('joinder_and'$this->locale);
  1061.         $ceramicTilesName $this->translate('catalog_ceramic_tiles'$this->locale);
  1062.         $sTtitle2 "";
  1063.         // type (вид изделия)
  1064.         if ($typeGroup $filterGroups->getProductType()) {
  1065.             $sTtitle2 $this->modifyFirstFilter($typeGroup);
  1066.         } else {
  1067.             // material
  1068.             if ($materialGroup $filterGroups->getMaterial()) {
  1069.                 $sTtitle2 $this->modifyFirstFilter($materialGroup);
  1070.             }
  1071.         }
  1072.         $groups $filterGroups->getGroups();
  1073.         $maxCntFilters 0;
  1074.         foreach ($groups as $group) {
  1075.             $cnt count($group->getListFilters());
  1076.             if ($cnt $maxCntFilters) {
  1077.                 $maxCntFilters $cnt;
  1078.             }
  1079.         }
  1080.         if ($maxCntFilters 2) {
  1081.             $separatorGroup ';';
  1082.             $separatorFilter ',';
  1083.         } else {
  1084.             $separatorGroup ',';
  1085.             $separatorFilter $and;
  1086.         }
  1087.         $cntGroup count($groups);
  1088.         $isDotBefore false;
  1089.         foreach ($groups as $i => $group) {
  1090.             $groupName $group->getGroupName();
  1091.             $altName $group->getGroupAltName();
  1092.             if ($i == || $isDotBefore) {
  1093.                 $groupName StrHelper::ucFirstOnly($groupName);
  1094.             } else {
  1095.                 $groupName StrHelper::toLower($groupName);
  1096.             }
  1097.             if ($altName == 'dimension' || $altName == 'rewards' || $altName == 'samples') {
  1098.                 $groupName '';
  1099.             } elseif ($altName == 'bm') {
  1100.                 $groupName StrHelper::toUpper($groupName) . ': ';
  1101.             } elseif ($altName == 'resistance_abrasion') {
  1102.                 $groupName StrHelper::toUpper($groupName);
  1103.             }
  1104.             $groupName = !empty($groupName) && $i "{$groupName} " "";
  1105.             if ($this->isOneFilter()) {
  1106.                 $groupName '';
  1107.             }
  1108.             $name_ '';
  1109.             $listFilters $group->getListFilters();
  1110.             $nameCnt count($listFilters);
  1111.             $prefGroup false;
  1112.             foreach ($listFilters as $ii => $filter) {
  1113.                 $pref '';
  1114.                 if ($ii 0) {
  1115.                     $pref $separatorFilter "{$separatorFilter} " ' ';
  1116.                     // для последнего ставим
  1117.                     if ($nameCnt == $ii) {
  1118.                         $pref {$and} ";
  1119.                     }
  1120.                     // для размеров убираем разделители между двумя одинаковами типами
  1121.                     if ($altName == 'dimension') {
  1122.                         if (!empty($listFilters[$ii 1])) {
  1123.                             $keyPrew $listFilters[$ii 1]->getKey();
  1124.                             if ($filter->getKey() == $keyPrew '2') {
  1125.                                 $pref ' ';
  1126.                             }
  1127.                         }
  1128.                     }
  1129.                 }
  1130.                 if ($i == 0) {
  1131.                     $elFilterName StrHelper::toLower($filter->getNameSingle() ?? '');
  1132.                 } else {
  1133.                     $elFilterName $filter->getTitle() ?? '';
  1134.                 }
  1135.                 if (!$groupName) {
  1136.                     if (($i == && $ii == 0) || $isDotBefore) {
  1137.                         $elFilterName StrHelper::ucFirstOnly($elFilterName);
  1138.                     } else {
  1139.                         $elFilterName StrHelper::toLower($elFilterName);
  1140.                     }
  1141.                 }
  1142.                 $isDotBefore preg_match('/\.\s?<?/'strip_tags($elFilterName));
  1143.                 $elFilterName $this->buildElemetFilter($elFilterName$filter->getSlug());
  1144.                 $name_ .= "{$pref}{$elFilterName}";
  1145.             }
  1146.             // фикс случайных нескольких пробелов на всякий случай
  1147.             $name_ preg_replace(['# {2,}#umi'], ' '$name_);
  1148.             if ($i == $cntGroup 1) {
  1149.                 $sepGroup '';
  1150.             } else {
  1151.                 $sepGroup "{$separatorGroup} ";
  1152.             }
  1153.             // если нашли вконце точку, то разделитель убираем
  1154.             if ($isDotBefore) {
  1155.                 $sepGroup '';
  1156.             }
  1157.             if ($prefGroup) {
  1158.                 $sTtitle2_pref "{$name_}. ";
  1159.             } else {
  1160.                 $sTtitle2 .= "{$groupName}{$name_}{$sepGroup}";
  1161.             }
  1162.         }
  1163.         $sTtitle2 = (!empty($sTtitle2_pref) ? $sTtitle2_pref '') . $sTtitle2;
  1164.         return $sTtitle2;
  1165.     }
  1166.     /**
  1167.      * @param string $name
  1168.      * @param string $slug
  1169.      * @return string
  1170.      */
  1171.     private function buildElemetFilter(string $namestring $slug): string
  1172.     {
  1173.         $link $this->generateUrl('app_catalog', ['key' => $slug]);
  1174.         return "<a class=\"el-filter\" href=\"{$link}\">{$name}</a>";
  1175.     }
  1176.     /**
  1177.      * @param int $id
  1178.      * @param string $key
  1179.      * @param string $nameFilterStr
  1180.      * @return string
  1181.      */
  1182.     private function addElemetFilterLinkEdit(int $idstring $keystring $nameFilterStr): string
  1183.     {
  1184.         $linkEdit $key == 'factory' Adm::linkEdit('adm.brand.edit'$id) : Adm::linkEdit('adm.filter.edit'$id);
  1185.         $linkEdit $linkEdit "<sup> <a class=\"link-edit\" href=\"$linkEdit\" target=\"_blank\">edit</a></sup>" null;
  1186.         return $linkEdit "{$nameFilterStr}{$linkEdit}$nameFilterStr;
  1187.     }
  1188.     /**
  1189.      * @return FilterGroupsCatalogDTO
  1190.      * @throws Exception
  1191.      */
  1192.     public function buildFilterGroupsDTO(): FilterGroupsCatalogDTO
  1193.     {
  1194.         $aCurFilters $this->getSortedFiltersLikeInTheLiteMenu();
  1195.         $isOneFilter $this->isOneFilter();
  1196.         //$isDesignerUser = $this->filterService()->isDesignerUser();
  1197.         $filterGroups = new FilterGroupsCatalogDTO();
  1198.         // если фильтр размеров есть, то создаем массив с количеством значений по группам, для будущего формирования
  1199.         $aDimensionsCnt = [];
  1200.         if ($aDimensions $this->getDimensions()) {
  1201.             foreach ($aDimensions as $key => $val) {
  1202.                 $key str_replace('2'''$key);
  1203.                 if (!empty($aDimensionsCnt[$key])) {
  1204.                     $aDimensionsCnt[$key] += 1;
  1205.                 } else {
  1206.                     $aDimensionsCnt[$key] = 1;
  1207.                 }
  1208.             }
  1209.         }
  1210.         foreach ($aCurFilters as $filter) {
  1211.             $groupAltName $filter->getGroupAltName();
  1212.             if (!$filterGroup $filterGroups->getGroup($groupAltName)) {
  1213.                 $filterGroup = new FilterGroupCatalogDTO($filter->getGroupId(), $filter->getGroupName(), $groupAltName);
  1214.                 $filterGroups->addGroup($filterGroup);
  1215.             }
  1216.             $nameSingle $filter->getNameSingle();
  1217.             $nameMany $filter->getNameMany();
  1218.             $dopValue null;
  1219.             $slug $filter->getSlug();
  1220.             if ($filter->isDimension()) {
  1221.                 $dopValue $this->aDimensions[$filter->getSphinxName()];
  1222.                 $slug "{$slug}-{$dopValue}";
  1223.             } elseif ($groupAltName == 'bm') {
  1224.                 $dopValue $this->aBMs['bm'];
  1225.                 $slug "{$slug}-{$dopValue}";
  1226.             } elseif ($filter->isAlsoCollViewed()) {
  1227.                 $dopValue $this->alsoCollViewedId;
  1228.             }
  1229.             $filterGroup->addListFilters(
  1230.                 new FilterCatalogDTO(
  1231.                     $filter->getId(),
  1232.                     $filter->getSphinxName(),
  1233.                     $filter->getSphinxId(),
  1234.                     $filter->getCode(),
  1235.                     $filter->getAltName(),
  1236.                     $slug,
  1237.                     $nameSingle,
  1238.                     $nameMany,
  1239.                     $dopValue,
  1240.                 )
  1241.             );
  1242.         }
  1243.         foreach ($filterGroups->getGroups() as $groupDto) {
  1244.             $groupAltName $groupDto->getGroupAltName();
  1245.             foreach ($groupDto->getListFilters() as $filterDto) {
  1246.                 $title $isOneFilter $filterDto->getNameSingle() : $filterDto->getNameMany();
  1247.                 $filterDto->setTitle($title);
  1248.                 switch ($groupAltName) {
  1249.                     case 'antislip_coff':
  1250.                         $groupName $this->translate('antislip_full_name'$this->locale, ['%d%' => '']);
  1251.                         $groupDto->setGroupName($groupName);
  1252.                         break;
  1253.                     case 'resistance_abrasion':
  1254.                         // $title = trim(str_replace('PEI', '', $title));
  1255.                         $filterDto->setTitle($title);
  1256.                         break;
  1257.                     case 'price':
  1258.                         $title LocaleHelper::getNamePriceFilter($title$this->locale);
  1259.                         $title LocaleHelper::getNamePriceFilterWithCurrency($title);
  1260.                         $filterDto->setTitle($title);
  1261.                         break;
  1262.                     case 'bm':
  1263.                         $repoUser App::getRepository('WebBundle:User');
  1264.                         $title $repoUser->getUserEasyNameById($filterDto->getDopValue());
  1265.                         $filterDto->setTitle($title);
  1266.                         break;
  1267.                     case 'top':
  1268.                         if ($filterDto->getKey() == 'getAlsoCollViewed') {
  1269.                             if ($collAlsoId $filterDto->getDopValue()) {
  1270.                                 /** @var Collection $coll */
  1271.                                 $coll $this->collRepo->getCollForAlsoById($collAlsoId);
  1272.                                 if (null === $coll) {
  1273.                                     throw new NotFoundHttpException();
  1274.                                 }
  1275.                                 $factory $coll->getFactory();
  1276.                                 $collName $coll->getName();
  1277.                                 $brandName $factory->getName();
  1278.                                 if ($coll->getStatus() == || $factory->getStatus() == 1) {
  1279.                                     $collUrl $this->generateUrl(
  1280.                                         'app_collection',
  1281.                                         ['factoryUrl' => $factory->getUrl(), 'collectionUrl' => $coll->getUrl()]
  1282.                                     );
  1283.                                     $collName "<a href=\"$collUrl\" title=\"{$coll->getName()}\">{$coll->getName()}</a>";
  1284.                                     $brandUrl $this->generateUrl('app_catalog', ['key' => $factory->getUrl()]);
  1285.                                     $brandName "<a href=\"$brandUrl\" title=\"$brandName\">$brandName</a>";
  1286.                                 }
  1287.                             } else {
  1288.                                 $collName $this->translate('collection');
  1289.                                 $brandName $this->translate('brand');
  1290.                             }
  1291.                             $title str_replace('%collection%'$collName$title);
  1292.                             if (stripos($title'%brand%') !== false) {
  1293.                                 $title str_replace('%brand%'$brandName$title);
  1294.                             }
  1295.                             $filterDto->setTitle($title);
  1296.                         }
  1297.                         break;
  1298.                     case 'dimension':
  1299.                         $sizeVal $aDimensions[$filterDto->getKey()];
  1300.                         $sizeEd $this->translate("left_menu_{$filterDto->getCode()}"$this->locale);
  1301.                         $dimKey str_replace('2'''$filterDto->getKey());
  1302.                         // если группа размеров содержит более одного элемента, формируем дополнительный массив
  1303.                         if ($aDimensionsCnt[$dimKey] > 1) {
  1304.                             $aSizeType explode(' '$title);
  1305.                             $firstTitle array_shift($aSizeType);
  1306.                             $pattern $this->translate('settings_size_from_to'$this->locale);
  1307.                             if ($pattern and $pattern != 'settings_size_from_to') {
  1308.                                 if (preg_match('/(.*%size%)\s(.*%size2%)/'$pattern$out)) {
  1309.                                     if (preg_match('/.*2$/'$filterDto->getKey())) {
  1310.                                         $title str_replace('%size2%'$sizeVal$out[2]);
  1311.                                         $title "{$title} {$sizeEd}";
  1312.                                     } else {
  1313.                                         $title str_replace('%size%'$sizeVal"{$out[1]} ");
  1314.                                         $title "{$firstTitle} {$title}";
  1315.                                     }
  1316.                                 }
  1317.                             }
  1318.                         } else {
  1319.                             $title "{$title} {$sizeVal} {$sizeEd}";
  1320.                         }
  1321.                         $filterDto->setTitle($title);
  1322.                         break;
  1323.                 }
  1324.             }
  1325.         }
  1326.         return $filterGroups;
  1327.     }
  1328.     /**
  1329.      * разбиваем строку кея на полстроки для дополнительного поиска. например для размеров или БМа
  1330.      * @param string $key
  1331.      * @return array
  1332.      */
  1333.     private function buldExplodeKeys(string $key)
  1334.     {
  1335.         $res = [];
  1336.         $aKey explode('-'$key);
  1337.         if (count($aKey) > 2) {
  1338.             // создаем копию массива и удаляем последний элемент
  1339.             $aKeyTmp $aKey;
  1340.             array_pop($aKeyTmp);
  1341.             $res[] = implode('-'$aKeyTmp);
  1342.             // создаем копию массива и удаляем последний элемент
  1343.             $aKeyTmp $aKey;
  1344.             array_shift($aKeyTmp);
  1345.             $res[] = implode('-'$aKeyTmp);
  1346.         }
  1347.         foreach ($aKey as $k) {
  1348.             if (!is_numeric($k) && mb_strlen($k'utf-8') > 2) {
  1349.                 $res[] = $k;
  1350.             }
  1351.         }
  1352.         // в конец добавим полный кей, для LIKE поиска
  1353.         $res[] = $key;
  1354.         return array_unique($res);
  1355.     }
  1356.     /**
  1357.      * Правим фильтр, который должен быть первым и определяющим для страницы
  1358.      * @param FilterGroupCatalogDTO $filterGroup
  1359.      * @return string
  1360.      */
  1361.     private function modifyFirstFilter(FilterGroupCatalogDTO $filterGroup): string
  1362.     {
  1363.         $tilesName $this->translate('footer_tile'$this->locale);
  1364.         if (count($filterGroup->getListFilters()) > 1) {
  1365.             return "{$tilesName}. ";
  1366.         }
  1367.         // ставим отдельным предложением в начало
  1368.         $afterDot $this->isOneFilter() ? '' '. ';
  1369.         $firstFilter $filterGroup->getFirstFilter();
  1370.         $firstFilter->setTitle("{$firstFilter->getNameSingle()}{$afterDot}");
  1371.         $filterGroup->setGroupName('');
  1372.         return '';
  1373.     }
  1374.     private function getSizeInCalc(array $input): array
  1375.     {
  1376.         $items array_keys($input);
  1377.         sort($items);
  1378.         $items array_map(function ($num) {
  1379.             $num = (int)$num;
  1380.             $num $num 100;
  1381.             $n round($num2PHP_ROUND_HALF_DOWN);
  1382.             return ($n == 0.0) ? null $n;
  1383.         }, $items);
  1384.         // Удаляем null'ы
  1385.         $items array_filter($items, function ($val) {
  1386.             return $val !== null;
  1387.         });
  1388.         return array_unique($items);
  1389.     }
  1390.     /**
  1391.      * Перенести из контроллера в сервис поиска, а лучше в сервис фильтров
  1392.      *
  1393.      * @return array|string
  1394.      * @throws Exception
  1395.      */
  1396.     public function getFiltersList()
  1397.     {
  1398.         $lc App::getCurLocale();
  1399.         $cc App::getCurCountry();
  1400.         $lcFull $lc == $cc $lc "{$lc}_$cc";
  1401.         $cur LocaleHelper::getCurrency();
  1402.         $msr LocaleHelper::getUserMeasure();
  1403.         $memcache App::getMemcache();
  1404.         $redisCachePool App::getContainer()->get(RedisCachePool::class)->getPool();
  1405.         //        $memcacheKey = "collection_filter_factory_search_page_list_$lcFull.$cur.$msr";
  1406.         //        $filters = $memcache->get($memcacheKey);
  1407.         //
  1408.         //        if ($filters) {
  1409.         //                  return $filters; //todo тест без кеша
  1410.         //        }
  1411.         $filters = [];
  1412.         $filtersTmp $this->filterRepo->getArrListForFilterMenuNew($lc);
  1413.         if ($lc == 'zh') {
  1414.             $filtersTmpEn $this->filterRepo->getArrListForFilterMenuNew('en');
  1415.             // Рекурсивно заполняем пустые поля китайского массива значениями из английского
  1416.             $filtersTmp $this->filterRepo->fillEmptyFields($filtersTmp$filtersTmpEn);
  1417.         }
  1418.         //приписываем новые значения Просьба:Подвисает отбор по отзывам на рабочем сайте
  1419.         $memKeyCalc 'all_calculate_' $cc '_' $msr;
  1420.         $allCalculateItem $redisCachePool->getItem($memKeyCalc);
  1421.         if ($allCalculateItem->isHit()) {
  1422.             $allCalculate $allCalculateItem->get();
  1423.             $sizesX $this->getSizeInCalc($allCalculate['size_x']);
  1424.             $sizesY $this->getSizeInCalc($allCalculate['size_y']);
  1425.             $thicks $this->getSizeInCalc($allCalculate['tolsch']);
  1426.             $filtersTmp $this->setNewCount($filtersTmp$allCalculateItem->get());
  1427.         } else {
  1428.             $allCalculate false;
  1429.             $thicks $this->articleRepo->getSizeValues('thick');
  1430.             $sizesY $this->articleRepo->getSizeValues('sizeY');
  1431.             $sizesX $this->articleRepo->getSizeValues('sizeX');
  1432.         }
  1433.         // проверяем наличие вложенных фильтров
  1434.         $sub = [];
  1435.         $pids array_filter($filtersTmp, function ($r) {
  1436.             return $r['pid'] != null;
  1437.         });
  1438.         if ($pids) {
  1439.             foreach ($pids as $id => $r) {
  1440.                 unset($filtersTmp[$id]);
  1441.                 $sub[$r['pid']][$id] = $r;
  1442.             }
  1443.         }
  1444.         foreach ($filtersTmp as $filter) {
  1445.             $fid $filter['id'];
  1446.             $groupAltName $filter['group']['altName'];
  1447.             // вставляем вложенные фильтры, если есть такие
  1448.             if (!empty($sub[$fid])) {
  1449.                 $filter['sub'] = $sub[$fid];
  1450.             }
  1451.             $filters[$groupAltName][$fid] = $filter;
  1452.             // формируем фильтры для БМов
  1453.             if ($groupAltName == 'bm') {
  1454.                 $filters['bm'] = [];
  1455.                 $aUsers $this->collRepo->getListActiveBM();
  1456.                 foreach ($aUsers as $id => $name) {
  1457.                     $filters['bm'][$id] = [
  1458.                         'fid' => $fid,
  1459.                         'id' => $id,
  1460.                         'name' => $name,
  1461.                         'altName' => $id,
  1462.                         'selected' => [],
  1463.                         'group' => $filter['group'],
  1464.                         'count' => 0,
  1465.                     ];
  1466.                 }
  1467.             }
  1468.             // формируем фильтры для размеров
  1469.             if ($groupAltName == 'dimension') {
  1470.                 // проверяем на Дюймы и если что конвертируем
  1471.                 if (LocaleHelper::measureGb()) {
  1472.                     $measure '″';
  1473.                     if (StrHelper::isInStr($filter['altName'], 'tolsch')) {
  1474.                         $koff ConversionHelper::convertInch(
  1475.                             0.1,
  1476.                             ConversionHelper::MM,
  1477.                             5
  1478.                         ); //mm так там выходит 0.00394
  1479.                     } else {
  1480.                         $koff ConversionHelper::convertInch(1.0ConversionHelper::CM); //cm
  1481.                     }
  1482.                 } else {
  1483.                     if (StrHelper::isInStr($filter['altName'], 'tolsch')) {
  1484.                         $measure 'left_menu_mm';
  1485.                     } else {
  1486.                         $measure 'left_menu_cm';
  1487.                     }
  1488.                     $measure $this->translate($measure);
  1489.                     $koff 1;
  1490.                 }
  1491.                 if (!StrHelper::isInStr($filter['altName'], '2')) {
  1492.                     $filters[$groupAltName][$fid]['subGroup'] = [
  1493.                         'name' => $this->translate('dimensions_from'),
  1494.                         'altName' => 'from',
  1495.                         'suff' => '',
  1496.                         'measure' => '',
  1497.                     ];
  1498.                 } else {
  1499.                     $filters[$groupAltName][$fid]['subGroup'] = [
  1500.                         'name' => $this->translate('dimensions_to'),
  1501.                         'altName' => 'to',
  1502.                         'suff' => '2',
  1503.                         'measure' => $measure,
  1504.                     ];
  1505.                 }
  1506.                 /** @var $item Article */
  1507.                 if (StrHelper::isInStr($filter['altName'], 'tolsch')) {
  1508.                     $subGroupName $this->translate('left_menu_thickness');
  1509.                     $filters[$groupAltName][$fid]['name'] = $subGroupName;
  1510.                     $sizes = [];
  1511.                     foreach ($thicks as $s) {
  1512.                         $sizes[] = [
  1513.                             'name' => $s $koff,
  1514.                             'val' => $s,
  1515.                         ];
  1516.                     }
  1517.                     if (!StrHelper::isInStr($filter['altName'], '2')) {
  1518.                         array_pop($sizes);
  1519.                     }
  1520.                     $filters[$groupAltName][$fid]['sizes'] = $sizes;
  1521.                     $filters[$groupAltName][$subGroupName][$fid] = $filters[$groupAltName][$fid];
  1522.                 }
  1523.                 if (StrHelper::isInStr($filter['altName'], 'size_y')) {
  1524.                     $subGroupName $this->translate('left_menu_height');
  1525.                     $filters[$groupAltName][$fid]['name'] = $subGroupName;
  1526.                     foreach ($sizesY as $s) {
  1527.                         $filters[$groupAltName][$fid]['sizes'][] = [
  1528.                             'name' => $s $koff,
  1529.                             'val' => $s,
  1530.                         ];
  1531.                     }
  1532.                     $filters[$groupAltName][$subGroupName][$fid] = $filters[$groupAltName][$fid];
  1533.                 }
  1534.                 if (StrHelper::isInStr($filter['altName'], 'size_x')) {
  1535.                     $subGroupName $this->translate('left_menu_width');
  1536.                     $filters[$groupAltName][$fid]['name'] = $subGroupName;
  1537.                     foreach ($sizesX as $s) {
  1538.                         $filters[$groupAltName][$fid]['sizes'][] = [
  1539.                             'name' => $s $koff,
  1540.                             'val' => $s,
  1541.                         ];
  1542.                     }
  1543.                     $filters[$groupAltName][$subGroupName][$fid] = $filters[$groupAltName][$fid];
  1544.                 }
  1545.                 unset($filters[$groupAltName][$fid]);
  1546.             }
  1547.         }
  1548.         // переносим пачку противоскольжения в groupList фильтра Противоскользящая
  1549.         //todo не нужно если перенести противоскользыщие фильтры в подгруппу 10146 в группу 401
  1550.         // UPDATE `filters` SET `parent_id` = '10146', `group_id` = 401 WHERE group_id=422
  1551.         // логика обработки в sphinx
  1552. //        if (!empty($filters['surface']) && !empty($filters['surface']['10146'])) {
  1553. //            if (!empty($filters['antislip_coff'])) {
  1554. //                $filters['surface']['10146']['groupList'] = $filters['antislip_coff'];
  1555. //                unset($filters['antislip_coff']);
  1556. //            }
  1557. //        }
  1558.         $paramGetCollections = [
  1559.             'order' => 'c.name, c.publishDate DESC, c.id',
  1560.             'onlyCF' => true,
  1561.             'leftCollectionCount' => true,
  1562.             'locale' => App::getCurLocale(),
  1563.         ];
  1564.         $aColls $this->collRepo->getCollections($paramGetCollections);
  1565.         //получаем фабрики, которые скрытые в стране
  1566.         $issetCollectionFactory $allCalculate array_keys($allCalculate['factory']) : [];
  1567.         $issetCollection $allCalculate array_keys($allCalculate['c_id']) : [];
  1568.         //$all_coll=[];
  1569.         //        foreach ($aColls as $coll){
  1570.         //            $all_coll[]=$coll['id'];
  1571.         //        }
  1572.         //        $result = array_diff($all_coll, $issetCollection);
  1573.         //
  1574.         //        App::debugExit($paramGetCollections,$result,count($issetCollection),count($all_coll), $issetCollection,$all_coll);
  1575.         foreach ($aColls as $coll) {
  1576.             if (empty($coll['brandUrl']) || empty($coll['url'])) {
  1577.                 continue;
  1578.             }
  1579.             if (!empty($issetCollectionFactory) && !in_array($coll['id'], $issetCollection)) {
  1580.                 //App::debugExit($coll, $issetCollection);
  1581.                 continue;
  1582.             }
  1583.             $filters['collection'][$coll['id']] = [
  1584.                 'id' => $coll['id'],
  1585.                 'url' => $coll['url'],
  1586.                 'name' => $coll['nameFull'],
  1587.                 'altName' => $coll['alternateName'],
  1588.                 'selected' => [],
  1589.                 'brand' => [
  1590.                     'id' => $coll['brandId'],
  1591.                     'url' => $coll['brandUrl'],
  1592.                 ],
  1593.                 'dataLink' => $this->generateUrl(
  1594.                     'app_collection',
  1595.                     ['factoryUrl' => $coll['brandUrl'], 'collectionUrl' => $coll['url']]
  1596.                 ),
  1597.                 'group' => [
  1598.                     'name' => App::getTranslator()->trans('left_menu_collections'),
  1599.                     'altName' => 'collections',
  1600.                 ],
  1601.             ];
  1602.             if (!empty($filters['brand'][$coll['brandId']])) {
  1603.                 if (!in_array($coll['id'], $filters['brand'][$coll['brandId']]['collection'])) {
  1604.                     $filters['brand'][$coll['brandId']]['collection'][] = $coll['id'];
  1605.                 }
  1606.             }
  1607.         }
  1608.         //удаляем бренды которые скрыты
  1609.         if (!empty($issetCollectionFactory)) {
  1610.             //Удаляем фабрики если есть скрытые бренды
  1611.             $old count($filters['brand']);
  1612.             foreach ($filters['brand'] as $brKey => $brV) {
  1613.                 if (!in_array($brV['id'], $issetCollectionFactory)) {
  1614.                     unset($filters['brand'][$brKey]);
  1615.                 }
  1616.             }
  1617.         }
  1618.         // сортируем фабрики по имени
  1619.         if (!empty($filters['brand'])) {
  1620.             $brandSort array_column($filters['brand'], 'name');
  1621.             array_multisort($brandSortSORT_ASC$filters['brand']);
  1622.         }
  1623.         // сортируем выставки по ID
  1624.         if (!empty($filters['exhibition'])) {
  1625.             $exhSort array_column($filters['exhibition'], 'id');
  1626.             array_multisort($exhSortSORT_DESC$filters['exhibition']);
  1627.         }
  1628.         // кешируем на продакшене
  1629.         //        if (App::isGeneral()) {
  1630.         //            $memcache->add($memcacheKey, $filters, MEMCACHE_COMPRESSED, (int)(TimeConstant::HOUR * 12));
  1631.         //        }
  1632.         return $filters;
  1633.     }
  1634.     public function setNewCount(array $filters, array $allCalculate): array
  1635.     {
  1636.         //меняем логику перебираем фильтры и ищем новое значения иначе 0
  1637.         foreach ($filters as $filter) {
  1638.             if (!empty($allCalculate[$filter['oldCommand']][$filter['id']])) {
  1639.                 $filters[$filter['id']]['count'] = $allCalculate[$filter['oldCommand']][$filter['id']];
  1640.             }
  1641. //            else {
  1642. //               // unset($filters[$filter['id']]);//=0; todo не все
  1643. //            }
  1644.         }
  1645. //        foreach ($allCalculate as $group) {
  1646. //            foreach ($group as $id => $count) {
  1647. //                if (!empty($filters[$id])) {
  1648. //                    $filters[$id]['count'] = $count;
  1649. //                }
  1650. //            }
  1651. //        }
  1652.         return $filters;
  1653.     }
  1654. }