src/WebBundle/Controller/FiltersController.php line 806

Open in your IDE?
  1. <?php
  2. namespace WebBundle\Controller;
  3. use Doctrine\DBAL\DBALException;
  4. use Exception;
  5. use FlexApp\Constant\TimeConstant;
  6. use FlexApp\Service\CacheService;
  7. use FlexApp\Service\RedisCachePool;
  8. use Import1CBundle\Helper\v3\BiConst;
  9. use Memcache;
  10. use Symfony\Component\HttpFoundation\JsonResponse;
  11. use Symfony\Component\HttpFoundation\Response;
  12. use WebBundle\Entity\Article;
  13. use WebBundle\Entity\Collection;
  14. use WebBundle\Entity\FilterEntity;
  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\Helper\SynchronizeFiltersHelper;
  22. use WebBundle\Repository\ArticleRepository;
  23. use WebBundle\Repository\CollectionRepository;
  24. use WebBundle\Repository\FilterHistorySearchRepository;
  25. use WebBundle\Repository\FilterRepository;
  26. use WebBundle\Repository\UserRepository;
  27. use WebBundle\Service\FiltersService;
  28. class FiltersController extends ExtendedController
  29. {
  30.     /** @required */
  31.     public CollectionRepository $collectionRepository;
  32.     /** @required */
  33.     public FilterRepository $filterRepository;
  34.     /** @required */
  35.     public FilterHistorySearchRepository $filterHistorySearchRepository;
  36.     /** @required */
  37.     public ArticleRepository $articleRepository;
  38.     /** @required */
  39.     public CacheService $cacheService;
  40.     public Memcache $memcached;
  41.     /**
  42.      * FilterController constructor.
  43.      * @param CacheService $cacheService
  44.      * @throws Exception
  45.      */
  46.     public function __construct(CacheService $cacheService)
  47.     {
  48.         parent::__construct();
  49.         $this->memcached $cacheService->getMemcache();
  50.     }
  51.     /**
  52.      * @return null|JsonResponse
  53.      * @throws Exception
  54.      */
  55.     public function ajaxAdvancedSearchAction()
  56.     {
  57.         $locale App::getCurLocale();
  58.         $func RequestHelper::get('func');
  59.         $respLink '';
  60.         $respCount 0;
  61.         $calculate = [];
  62.         $respDisabledFilters = [];
  63.         $dataCollection = [];
  64.         $isInteriors false;
  65.         if ($func == 'getLinkHistory') {
  66.             $hash RequestHelper::get('hash');
  67.             if (!$hash) {
  68.                 return $this->error('Not found hash.'404true);
  69.             }
  70.             $row $this->filterHistorySearchRepository->getRowByHash($hash);
  71.             if (!$row) {
  72.                 $row_ $this->filterHistorySearchRepository->getRowByHash($hashfalse);
  73.                 if ($row_) {
  74.                     return $this->error('Access denied.'403true);
  75.                 }
  76.                 return $this->error('Not found history.'404true);
  77.             }
  78.             $fids $row['filterIds'];
  79.             $filterService App::getContainer()->get('app.service.filters');
  80.             if ($row['collId']) {
  81.                 $filterService->loadByIds($fids$locale);
  82.                 $coll $this->collectionRepository->getCollectionsForMenuFilters(['id' => $row['collId']]);
  83.                 $coll count($coll) == $coll[0] : null;
  84.                 /** @var Collection $coll */
  85.                 if (!$coll) {
  86.                     return $this->error('Not found collection.'404true);
  87.                 }
  88.                 $brand $coll->getFactory();
  89.                 /** @var FilterEntity $brand */
  90.                 if (!$brand) {
  91.                     return $this->error('Not found brand.'404true);
  92.                 }
  93.                 $brandUrl $brand->getFilter()->getUrl()->{'get' ucfirst($locale)}();
  94.                 $collUrl $coll->getUrl();
  95.                 $attrUrl = [
  96.                     'factoryUrl' => $brandUrl,
  97.                     'collectionUrl' => $collUrl,
  98.                 ];
  99.                 // прикручиваем строки фильтрации к URL коллекции
  100.                 if ($fKey $filterService->getWord()) {
  101.                     $fKey str_replace($attrUrl['factoryUrl'], ''$fKey);
  102.                     $fKey trim($fKey'&');
  103.                     if ($fKey) {
  104.                         $attrUrl['type'] = 'f';
  105.                         $attrUrl['elementId'] = $fKey;
  106.                     }
  107.                 }
  108.                 $respLink $this->generateUrl('app_collection'$attrUrl);
  109.             } else {
  110.                 if ($row['brandId']) {
  111.                     $fids[] = $row['brandId'];
  112.                 }
  113.                 $filterService->loadByIds($fids$locale);
  114.                 $respLink $filterService->getUrl();
  115.             }
  116.             return $this->ok(['link' => $respLink'hash' => $hash], true);
  117.         }
  118.         if ($func == 'saveHistory') {
  119.             $hasLiteMenu RequestHelper::get('lmenu') == 1;
  120.             $rows RequestHelper::get('data');
  121.             if (!$rows) {
  122.                 return $this->error('Not found values data.'404true);
  123.             }
  124.             $rows json_decode($rowstrue);
  125.             $collId null;
  126.             $brandId null;
  127.             if (!empty($rows['collection'][0][1])) {
  128.                 $collId $rows['collection'][0][1];
  129.                 unset($rows['collection']);
  130.             }
  131.             if (!empty($rows['brand'][0][1])) {
  132.                 $brandId $rows['brand'][0][1];
  133.                 unset($rows['brand']);
  134.             }
  135.             $fids = [];
  136.             if ($hasLiteMenu) {
  137.                 // логика под короткое меню
  138.                 /** @var $repoFilter FilterRepository */
  139.                 $repoFilter App::getRepository('WebBundle:FilterEntity');
  140.                 if (!empty($rows['factory'][0])) {
  141.                     $brandId $repoFilter->getFilterIdByOldId((int)$rows['factory'][0], 'factory');
  142.                     unset($rows['factory']);
  143.                 }
  144.                 foreach ($rows as $oldCommand => $oldIds) {
  145.                     $oldIds array_filter($oldIds);
  146.                     if ($oldIds) {
  147.                         $fids_ $repoFilter->getFilterIdsByOldIds($oldIds$oldCommand);
  148.                         if ($fids_) {
  149.                             $fids array_merge($fids$fids_);
  150.                         }
  151.                     }
  152.                 }
  153.             } else {
  154.                 foreach ($rows as $els) {
  155.                     foreach ($els as $el) {
  156.                         $fids[] = $el[1];
  157.                     }
  158.                 }
  159.             }
  160.             $fids array_unique($fids);
  161.             $this->filterHistorySearchRepository->setHistory($collId$brandId$fids);
  162.             $data = [
  163.                 'state' => 'saved',
  164.                 'list' => $this->getListHistory(),
  165.             ];
  166.             return $this->ok($datatrue);
  167.         }
  168.         if ($func == 'gethistory') {
  169.             $data = [
  170.                 'state' => 'get_history',
  171.                 'list' => $this->getListHistory(),
  172.             ];
  173.             return $this->ok($datatrue);
  174.         }
  175.         if ($func == 'delLinkHistory') {
  176.             $hash RequestHelper::get('hash');
  177.             if (!$hash) {
  178.                 return $this->error('Not found hash.'404true);
  179.             }
  180.             $this->filterHistorySearchRepository->delHistory($hash);
  181.             $data = [
  182.                 'state' => 'deleted',
  183.                 'list' => $this->getListHistory(),
  184.             ];
  185.             return $this->ok($datatrue);
  186.         }
  187.         if ($func == 'getLink') {
  188.             $data RequestHelper::get('data', []);
  189.             // обработка запроса коллекций
  190.             if (!empty($data['collection'])) {
  191.                 $collId array_shift($data['collection']);
  192.                 $collId = !empty($collId['val']) ? $collId['val'] : null;
  193.                 if (!$collId) {
  194.                     return $this->error('ID коллекции не получен');
  195.                 }
  196.                 $coll $this->collectionRepository->getCollectionsForMenuFilters(['id' => $collId]);
  197.                 $coll count($coll) == $coll[0] : null;
  198.                 /** @var Collection $coll */
  199.                 if (!$coll) {
  200.                     return $this->error('Коллекция не найдена');
  201.                 }
  202.                 $factoryUrl null;
  203.                 if (!empty($data['brand'])) {
  204.                     $brandId ArrHelper::get($data'brand.brand.val');
  205.                     if (!$brandId) {
  206.                         return $this->error('ID фабрики не получен');
  207.                     }
  208.                     $data[] = $data['brand'];
  209.                     unset($data['brand']);
  210.                     unset($data['collection']);
  211.                     /** @var FilterRepository $fRepo */
  212.                     $fRepo App::getRepository('WebBundle:FilterEntity');
  213.                     $brand $fRepo->getBrand(['id' => $brandId]);
  214.                     $brand count($brand) == $brand[0] : null;
  215.                     /** @var FilterEntity $brand */
  216.                     if (!$brand) {
  217.                         return $this->error('Фабрика не найдена');
  218.                     }
  219.                     $factoryUrl $brand->getUrl()->{'get' ucfirst($locale)}();
  220.                 }
  221.                 if (!$factoryUrl) {
  222.                     $factoryUrl $coll->getFactory()->getFilter()->getUrl()->{'get' ucfirst($locale)}();
  223.                 }
  224.                 $dataCollection = [
  225.                     'c_id' => [$coll->getId()],
  226.                     'attrUrl' => [
  227.                         'factoryUrl' => $factoryUrl,
  228.                         'collectionUrl' => $coll->getUrl(),
  229.                     ],
  230.                 ];
  231.             }
  232.             /**
  233.              * обработка остальных запросов
  234.              */
  235.             $ids = [];
  236.             foreach ($data as $items) {
  237.                 foreach ($items as $item) {
  238.                     $ids[] = $item['val'];
  239.                 }
  240.             }
  241.             $lc App::getCurLocale();
  242.             $cc App::getCurCountry();
  243.             $lcFull $lc == $cc $lc "{$lc}_$cc";
  244.             $memcachedKey 'collection_filter_factory_AdvancedSearch_null_' $lcFull;
  245.             $resCache $this->memcached->get($memcachedKey);
  246.             if ($resCache and count($ids) < and !$dataCollection) {
  247.                 $resCache json_decode($resCachetrue);
  248.                 $respLink $resCache['respLink'];
  249.                 $respCount $resCache['respCount'];
  250.                 $respDisabledFilters $resCache['filtered'];
  251.             } else {
  252.                 $filterService App::getContainer()->get('app.service.filters');
  253.                 $filterService->loadByIds($ids$locale);
  254.                 if ($dataCollection) {
  255.                     $attrUrl $dataCollection['attrUrl'];
  256.                     // прикручиваем строки фильтрации к URL коллекции
  257.                     $fKey $filterService->getWord();
  258.                     if ($fKey) {
  259.                         $fKey str_replace($attrUrl['factoryUrl'], ''$fKey);
  260.                         $fKey trim($fKey'&');
  261.                         $fKey str_replace('&&''&'$fKey);
  262.                         if ($fKey) {
  263.                             $attrUrl['type'] = 'f';
  264.                             $attrUrl['elementId'] = $fKey;
  265.                         }
  266.                     }
  267.                     $respLink $this->generateUrl('app_collection'$attrUrl);
  268.                 } else {
  269.                     $respLink $filterService->getUrl();
  270.                 }
  271.                 $data = [
  272.                     'searchFilter' => [],
  273.                     'searchSort' => 1,
  274.                     'searchPeriod' => null,
  275.                     'locale' => $locale,
  276.                 ];
  277.                 $filters $filterService->getCurFilters();
  278.                 foreach ($filters as $filter) {
  279.                     if ($filter->isDimension()) {
  280.                         $searchFilterVal $filterService->getDimensions()[$filter->getAltName()];
  281.                     } elseif ($filter->isBM()) {
  282.                         $searchFilterVal $filterService->getBMs()[$filter->getAltName()];
  283.                     } else {
  284.                         $searchFilterVal $filter->getSphinxId();
  285.                     }
  286.                     $data['searchFilter'][$filter->getSphinxName()][] = $searchFilterVal;
  287.                 }
  288.                 if ($dataCollection) {
  289.                     $data['searchFilter']['c_id'] = $dataCollection['c_id'];
  290.                     $data['searchFilter']['inside'] = true;
  291.                 }
  292.                 $data['total_found'] = 1;
  293.                 $searchResult App::searchSphinx($data0falsefalse, !empty($data['calculate']));
  294.                 //если запросили подсчет
  295.                 if (!empty($data['calculate'])) {
  296.                     $calculate $searchResult['calculate'];
  297.                     $aRes $searchResult['collections'];
  298.                 } else {
  299.                     $aRes $searchResult;
  300.                 }
  301.                 if ($dataCollection) {
  302.                     $aRes = !empty($aRes[0]['articles'])
  303.                         ? array_values(array_unique(array_column($aRes[0]['articles'], 'a_id')))
  304.                         : [];
  305.                     if (!$aRes) {
  306.                         $aRes = !empty($searchTrue[0]['interiors'])
  307.                             ? array_values(array_unique(array_column($searchTrue[0]['interiors'], 'i_id')))
  308.                             : [];
  309.                         $isInteriors true;
  310.                     }
  311.                 }
  312.                 $respCount = isset($searchResult['total_found']) ? $searchResult['total_found'] : count($aRes);
  313.                 // если есть выбранная коллекция, то показываем в количестве ее одну или ничего
  314.                 if ($dataCollection) {
  315.                     $respCount $respCount 0;
  316.                 }
  317.                 // проверяем результат сочетаний с другими фильтрами
  318.                 if (count($ids) > 0) {
  319.                     //todo вынести в отдельную команду так как кешируеться на всех зеркалах а просчитаться может на одном
  320.                     // проверка на количество коллекций в поиске с сохранением в поле фильтра
  321. //                    if (count($filters) == 1) {
  322. //                        $filter = $filters[0];
  323. //                        $filterResCount = 0;
  324. //                        if ($filter instanceof FilterEntity) {
  325. //                            $filterResCount = $filter->getParam()->getCount();
  326. //                        } elseif ($filter instanceof FilterResponseDTO) {
  327. //                            $filterResCount = $filter->getCount();
  328. //                        }
  329. //                        if ($filter && $respCount != $filterResCount) {
  330. //                            SynchronizeFiltersHelper::runSynchCountFilters($filter->getId());
  331. //                        }
  332. //                    }
  333.                     if ($checkIds RequestHelper::get('checkIds', [])) {
  334.                         /** @var FilterRepository $fRepo */
  335.                         $fRepo App::getRepository('WebBundle:FilterEntity');
  336.                         $filtersCheck $fRepo->getByIds($checkIds);
  337.                         foreach ($filtersCheck as $filter) {
  338.                             if (!in_array($filter->getId(), $ids)) {
  339.                                 $dataSS $data;
  340.                                 $dataSS['searchFilter'][$filter->getOldCommand()][] = $filter->getOldId();
  341.                                 $aRes App::searchSphinx($dataSS3);
  342.                                 if (count($aRes) < 1) {
  343.                                     $respDisabledFilters['ids'][] = $filter->getId();
  344.                                 }
  345.                             }
  346.                         }
  347.                     }
  348.                 } else {
  349.                     // если массив запросов пустой, то пишем данные в кеш и оттуда будем выдавать //Todo а на всякий случай загоним не тут а при открытии фильтра
  350.                     $data = [
  351.                         'respLink' => $respLink,
  352.                         'respCount' => $respCount,
  353.                         'filtered' => $respDisabledFilters,
  354.                     ];
  355.                     $data json_encode($data);
  356.                     // кешируем на продакшене
  357.                     if (App::isGeneral()) {
  358.                         $this->memcached->add($memcachedKey$datafalse, (int)TimeConstant::HOUR 12);
  359.                     }
  360.                 }
  361.             }
  362.         }
  363.         if ($dataCollection) {
  364.             if ($isInteriors) {
  365.                 $countPluralType 'interior';
  366.             } else {
  367.                 $countPluralType 'articles';
  368.             }
  369.         } else {
  370.             $countPluralType 'collection';
  371.         }
  372.         $btnInfo $this->translate('left_menu_show');
  373.         $response = [
  374.             'link' => urldecode($respLink),
  375.             'count' => $respCount,
  376.             'btnInfo' => $btnInfo,
  377.             'filtered' => $respDisabledFilters,
  378.             'countPluralType' => $countPluralType,
  379.             'calculate' => $calculate,
  380.         ];
  381.         return $this->ok($responsetrue);
  382.     }
  383.     /**
  384.      * проставляем выбранные фильтры
  385.      */
  386.     private function setFilterSelected(array $selectedstring $alt, array $filters): array
  387.     {
  388.         $selectedReact = [];
  389.         foreach ($filters as $i => $item) {
  390.             if ($alt == 'collection') {
  391.                 foreach ($selected['collections'] as $k => $v) {
  392.                     if (StrHelper::toLower($item['url']) == StrHelper::toLower($k)) {
  393.                         if (StrHelper::toLower($item['brand']['url']) == StrHelper::toLower($selected['brand'])) {
  394.                             $filters[$i]['selected'][] = $item['id'];
  395.                             $selectedReactItem = [];
  396.                             $selectedReactItem[] = 'collection';
  397.                             $selectedReactItem[] = $item['id'];
  398.                             $selectedReact[] = $selectedReactItem;
  399.                         }
  400.                     }
  401.                 }
  402.             } else {
  403.                 foreach ($selected['filters'] as $k => $v) {
  404.                     if (!empty($item['groupList'])) {
  405.                         $response $this->setFilterSelected($selected$alt$item['groupList']);
  406.                         $filters[$i]['groupList'] = $response['items'];
  407.                         $selectedSubFiltersReact $response['selectedReact'];
  408.                     }
  409.                     if (!empty($item['sub'])) {
  410.                         $response $this->setFilterSelected($selected$alt$item['sub']);
  411.                         $filters[$i]['sub'] = $response['items'];
  412.                         $selectedSubFiltersReact $response['selectedReact'];
  413.                     }
  414.                     if ($alt == 'dimension') {
  415.                         foreach ($item as $ii => $d) {
  416.                             if (strripos($k$d['key']) !== false) {
  417.                                 $val trim(str_replace($d['key'], ''$k), '-');
  418.                                 $filters[$i][$ii]['selected'][] = $val;
  419.                                 $selectedReactItem = [];
  420.                                 $selectedReactItem[] = $d['altName'];
  421.                                 $selectedReactItem[] = "{$d['id']}::{$val}";
  422.                                 $selectedReact[] = $selectedReactItem;
  423.                             }
  424.                         }
  425.                     } elseif ($alt == 'bm') {
  426.                         if (preg_match("/.*-{$item['id']}$/"$k)) {
  427.                             $filters[$i]['selected'][] = $item['id'];
  428.                             $selectedReactItem = [];
  429.                             $selectedReactItem[] = 'bm';
  430.                             $selectedReactItem[] = "{$item['fid']}::{$item['id']}";
  431.                             $selectedReact[] = $selectedReactItem;
  432.                         }
  433.                     } elseif ($item['key'] == $k) {
  434.                         $filters[$i]['selected'][] = $item['id'];
  435.                         $selectedReactItem = [];
  436.                         if ($alt == 'brand') {
  437.                             $selectedReactItem[] = 'brand';
  438.                         } else {
  439.                             $selectedReactItem[] = $item['name'];
  440.                         }
  441.                         $selectedReactItem[] = $item['id'];
  442.                         $selectedReact[] = $selectedReactItem;
  443.                     }
  444.                 }
  445.                 // добавляем выбранные вложенные фильтры
  446.                 if (!empty($selectedSubFiltersReact) && (!empty($item['sub']) || !empty($item['groupList']))) {
  447.                     $selectedReact array_merge($selectedReact$selectedSubFiltersReact);
  448.                 }
  449.             }
  450.         }
  451.         return [
  452.             'items' => $filters,
  453.             'selectedReact' => $selectedReact,
  454.         ];
  455.     }
  456.     /**
  457.      * карта фильтров
  458.      * @param $filters
  459.      * @return array|string
  460.      * @throws Exception
  461.      */
  462.     private function getFiltersMap($filters)
  463.     {
  464.         $lc App::getCurLocale();
  465.         $cur LocaleHelper::getCurrency();
  466.         $msr LocaleHelper::getUserMeasure();
  467.         $memcacheKey "collection_filter_factory_search_page_map_{$lc}.{$cur}.{$msr}";
  468.         $map $this->memcached->get($memcacheKey);
  469.         if (!$map) {
  470.             $groups = [];
  471.             $filtersTmp $this->filterRepository->getArrListForFilterMenuNew($lc);
  472.             foreach ($filtersTmp as $filter) {
  473.                 $groupAltName $filter['group']['altName'];
  474.                 $groups[$groupAltName] = $filter['group'];
  475.             }
  476.             $groups['collection'] = [
  477.                 'name' => App::getTranslator()->trans('left_menu_collections'),
  478.                 'altName' => 'collections',
  479.             ];
  480.             // для категории цены вывел вылюту
  481.             $priceName $groups['price']['name'];
  482.             $priceName $priceName ', ' html_entity_decode($cur) . '/' $msr '²';
  483.             $map = [
  484.                 'coll_1' => [
  485.                     'brand' => $groups['brand']['name'],
  486.                     'collection' => $groups['collection']['name'],
  487.                     'bm' => $groups['bm']['name'],
  488.                     'effect' => $groups['effect']['name'],
  489.                     'style' => $groups['style']['name'],
  490.                     'price' => $priceName,
  491.                 ],
  492.                 'coll_2' => [
  493.                     'color' => $groups['color']['name'],
  494.                     'using' => $groups['using']['name'],
  495.                     'material' => $groups['material']['name'],
  496.                 ],
  497.                 'coll_3' => [
  498.                     'samples' => !empty($groups['samples']) && $groups['samples']['name'],
  499.                     'type' => $groups['type']['name'],
  500.                     'surface' => $groups['surface']['name'],
  501.                     'apply' => $groups['apply']['name'],
  502.                 ],
  503.                 'coll_4' => [
  504.                     'shape' => $groups['shape']['name'],
  505.                     'motiv' => $groups['motiv']['name'],
  506.                     'country' => $groups['country']['name'],
  507.                 ],
  508.                 'coll_5' => [
  509.                     'dimension' => $groups['dimension']['name'],
  510.                     'granite' => $groups['granite']['name'],
  511.                     'edge_type' => $groups['edge_type']['name'],
  512.                     'other' => $groups['other']['name'],
  513.                     'rewards' => $groups['rewards']['name'],
  514.                     'reviews' => $groups['reviews']['name'],
  515.                 ],
  516.             ];
  517.             // кешируем на продакшене
  518.             if (App::isGeneral()) {
  519.                 $this->memcached->add($memcacheKey$mapMEMCACHE_COMPRESSED600);
  520.             }
  521.         }
  522.         // убираем из карты поля, под которые нет фильтров
  523.         foreach ($map as $col => $row) {
  524.             foreach ($row as $alt => $item) {
  525.                 if (empty($filters[$alt])) {
  526.                     unset($map[$col][$alt]);
  527.                 }
  528.             }
  529.         }
  530.         return $map;
  531.     }
  532.     /**
  533.      * Формирование страницы расширенных фильтров
  534.      * @return array
  535.      * @throws Exception
  536.      */
  537.     private function getAdvancedSearchPageData(): array
  538.     {
  539.         $locale App::getCurLocale();
  540.         $curCountry LocaleHelper::getCurCountry();
  541.         // формируем выбранные фильтры из строки запроса
  542.         $selected = [
  543.             'filters' => [],
  544.             'collections' => [],
  545.             'brand' => null,
  546.         ];
  547.         $uri urldecode(RequestHelper::syRequest()->getRequestUri());
  548.         $uri parse_url($uriPHP_URL_QUERY);
  549.         $uri str_replace('c=/'''$uri);
  550.         $uri trim($uri'/');
  551.         $uri StrHelper::toLower($uri);
  552.         $uri explode('/'$uri);
  553.         array_shift($uri);
  554.         if (count($uri) > 1) {
  555.             if ($uri[0] == 'catalogue') {
  556.                 $uri $uri[1];
  557.                 foreach (explode('&'$uri) as $item) {
  558.                     $selected['filters'][$item] = $item;
  559.                 }
  560.             }
  561.             if ($uri[0] == 'tile') {
  562.                 $selected['filters'][$uri[1]] = $uri[1];
  563.                 $selected['brand'] = $uri[1];
  564.                 $selected['collections'][$uri[2]] = $uri[2];
  565.                 // получение фильтров для коллекций
  566.                 if ($uriFilters ArrHelper::get($uri4)) {
  567.                     foreach (explode('&'$uriFilters) as $item) {
  568.                         $selected['filters'][$item] = $item;
  569.                     }
  570.                 }
  571.             }
  572.         }
  573.         $filterService = new FiltersService();
  574.         $filtersList $filterService->getFiltersList();
  575.         // удаляем БМом, если роль не позволяет
  576.         if (!$this->isGranted('ROLE_BM')) {
  577.             unset($filtersList['bm']);
  578.         }
  579.         // формируем выбранные фильтры из строки
  580.         $selectedFilters = [];
  581.         $selectedFiltersQty 0;
  582.         foreach ($filtersList as $alt => $items) {
  583.             $filterSelectedResponse $this->setFilterSelected($selected$alt$items);
  584.             $filtersList[$alt] = $filterSelectedResponse['items'];
  585.             $selectedFilters[$alt] = $filterSelectedResponse['selectedReact'];
  586.             $selectedFiltersQty += count($filterSelectedResponse['selectedReact']);
  587.         }
  588.         // формируем фильтры цен
  589.         foreach ($filtersList['price'] as $i => $item) {
  590.             if ($curCountry != $locale) {
  591.                 $name LocaleHelper::getNamePriceFilter($item['name'], $locale$curCountry);
  592.                 $filtersList['price'][$i]['name'] = $name;
  593.             }
  594.         }
  595.         $filtersMap $this->getFiltersMap($filtersList);
  596.         $isNullSelected = !(count($selected['collections']) > or count($selected['filters']) > 0);
  597.         $resLink null;
  598.         if ($isNullSelected) {
  599.             $resLink $this->generateUrl('app_catalog');
  600.         }
  601.         $lc App::getCurLocale();
  602.         $cc App::getCurCountry();
  603.         $lcFull $lc == $cc $lc "{$lc}_$cc";
  604.         $memcachedKey 'collection_filter_factory_AdvancedSearch_null_' $lcFull;
  605.         $resCache $this->memcached->get($memcachedKey);
  606.         if (!$resCache) {
  607.             $cntCollAll App::getContainer()->get('app.service.collection')->countNotFilteredCollection();
  608.             $data = [
  609.                 'respLink' => $resLink,
  610.                 'respCount' => $cntCollAll,
  611.                 'filtered' => [],
  612.             ];
  613.             $data json_encode($data);
  614.             // кешируем на продакшене
  615.             if (App::isGeneral()) {
  616.                 $this->memcached->add($memcachedKey$datafalse, (int)TimeConstant::HOUR 12);
  617.             }
  618.         } else {
  619.             $resCache json_decode($resCachetrue);
  620.             $cntCollAll $resCache['respCount'];
  621.         }
  622.         //todo дублируется выдача при выборе группы
  623.         foreach ($filtersList as $groupKey => &$group) {
  624.             foreach ($group as $itemKey => &$item) {
  625.                 if (!empty($item['selected']) && isset($item['sub']) && !empty($item['sub'])) {
  626.                     $selectedFiltersQty--; // :) а вот так :) иначе фронт ляжет
  627.                     $item['selected'] = []; //пробовал удалять из выбранных родителя не помогло
  628.                     //удаляем родителя
  629.                     $value_to_remove $item['id']; // Переменная с переданным значением
  630.                     $selectedFilters[$groupKey] = array_filter(
  631.                         $selectedFilters[$groupKey],
  632.                         function ($item_) use ($value_to_remove) {
  633.                             return $item_[1] != $value_to_remove;
  634.                         }
  635.                     );
  636.                     $selectedFilters[$groupKey] = array_values($selectedFilters[$groupKey]);
  637.                     foreach ($item['sub'] as $subKey => &$subItem) {
  638.                         $subItem['selected'] = [$subItem['id']];
  639.                         $selected['filters'][$subItem['key']] = $subItem['key'];
  640.                         $selectedFilters[$groupKey][] = [$subItem['name'], $subItem['id']];
  641.                         $selectedFiltersQty++;
  642.                     }
  643.                 }
  644.             }
  645.         }
  646.         return [
  647.             'action' => urldecode($this->generateUrl('app_filters_advanced_search_json')),
  648.             'map' => $filtersMap,
  649.             'filters' => $filtersList,
  650.             'selected' => $selected,
  651.             'cntCollAll' => $cntCollAll,
  652.             'resLink' => $resLink,
  653.             'isNullSelected' => $isNullSelected,
  654.             'noFloor' => true,
  655.             'selectedFilters' => $selectedFilters,
  656.             'selectedFiltersQty' => $selectedFiltersQty,
  657.             'countPluralType' => 'collection',
  658.         ];
  659.     }
  660.     /**
  661.      * @param bool $noResponse
  662.      * @return array|null|string|JsonResponse
  663.      * @throws Exception
  664.      */
  665.     public function ajaxListMenuFactoryAction(bool $noResponse false)
  666.     {
  667.         //todo скрыть скрытые фабрики
  668.         $locale App::getCurLocale();
  669.         $cacheKey "left_menu_lite_factory_filter_{$locale}_1" App::getCurCountry();
  670.         $factories App::getMemcache()->get($cacheKey);
  671.         $redisCachePool App::getContainer()->get(RedisCachePool::class)->getPool();
  672.         $cc App::getCurCountry();
  673.         $msr LocaleHelper::getUserMeasure();
  674.         $memKeyCalc 'all_calculate_' $cc '_' $msr//
  675.         $allCalculateItem $redisCachePool->getItem($memKeyCalc);
  676.         if ($allCalculateItem->isHit()) {
  677.             $allCalculate $allCalculateItem->get();
  678.         } else {
  679.             $allCalculate false;
  680.         }
  681.         $issetCollectionFactory $allCalculate array_keys($allCalculate['factory']) : [];
  682.         if (!$factories || App::isDev()) {
  683.             $translator $this->get('translator');
  684.             /** @var FilterRepository $repoFilter */
  685.             $repoFilter App::getRepository('WebBundle:FilterEntity');
  686.             $brands $repoFilter->getListBrandsForMenu($locale$translator);
  687.             foreach ($brands as $i => $brand) {
  688.                 if (!empty($issetCollectionFactory) && !in_array($brand['filter_id'], $issetCollectionFactory)) {
  689.                     unset($brands[$i]);
  690.                 }
  691.             }
  692.             $factoryAllCount count($brands);
  693.             if ($factoryAllCount 0) {
  694.                 $factories = [
  695.                     'data' => $brands,
  696.                     'all' => $factoryAllCount,
  697.                     'to' => 'brand',
  698.                 ];
  699.                 App::getMemcache()->set($cacheKey$factoriesfalseTimeConstant::DAY);
  700.             }
  701.         }
  702.         if ($noResponse) {
  703.             $response $factories;
  704.         } else {
  705.             $response $this->ok($factoriestrue);
  706.         }
  707.         return $response;
  708.     }
  709.     /**
  710.      * @param bool $noResponse
  711.      * @param null $brands
  712.      * @return array|null|string|JsonResponse
  713.      * @throws Exception
  714.      */
  715.     public function ajaxListMenuCollectionAction($noResponse false$brands null)
  716.     {
  717.         $collectionId RequestHelper::get('collectionId'null);
  718.         $_locale App::getCurLocale('full');
  719.         $redisCachePool App::getContainer()->get(RedisCachePool::class)->getPool();
  720.         $cc App::getCurCountry();
  721.         $msr LocaleHelper::getUserMeasure();
  722.         $memKeyCalc 'all_calculate_' $cc '_' $msr//
  723.         $allCalculateItem $redisCachePool->getItem($memKeyCalc);
  724.         if ($allCalculateItem->isHit()) {
  725.             $allCalculate $allCalculateItem->get();
  726.         } else {
  727.             $allCalculate false;
  728.         }
  729.         $issetCollectionFactory $allCalculate array_keys($allCalculate['factory']) : [];
  730.         $issetCollection $allCalculate array_keys($allCalculate['c_id']) : [];
  731.         $memcacheName 'left_menu_collection.0.' $collectionId $_locale . (App::isRole('ROLE_TEST') ? 0);
  732.         $collections $this->memcached->get($memcacheName);
  733.         if (!$collections || App::isDev()) {
  734.             $isStatusWork $this->collectionRepository->findOneBy([
  735.                 'status' => BiConst::STATE_DISCONTINUED,
  736.                 'id' => $collectionId,
  737.             ]);
  738.             $collectionId = ($isStatusWork) ? $collectionId null;
  739.             $param = [
  740.                 'order' => 'c.name, f.name',
  741.                 'onlyCF' => true,
  742.                 'collectionId' => $collectionId,
  743.                 'leftCollectionCount' => true,
  744.                 'locale' => App::getCurLocale(),
  745.             ];
  746.             if ($brands) {
  747.                 foreach ($brands as $brand) {
  748.                     $param['factoryId'][] = $brand['id'];
  749.                 }
  750.             }
  751.             $aColls $this->collectionRepository->getListForMenu($param);
  752.             foreach ($aColls as $i => $coll) {
  753.                 if (!empty($issetCollectionFactory) && !in_array($coll['id'], $issetCollection)) {
  754.                     unset($aColls[$i]);
  755.                     continue;
  756.                 }
  757.                 $coll['dataLink'] = $this->generateUrl(
  758.                     'app_collection',
  759.                     [
  760.                         'factoryUrl' => $coll['factory']['slug'],
  761.                         'collectionUrl' => $coll['slug'],
  762.                     ]
  763.                 );
  764.                 $aColls[$i] = $coll;
  765.             }
  766.             $collectionAllCount count($aColls);
  767.             if ($collectionAllCount 0) {
  768.                 $collections = [
  769.                     'data' => $aColls,
  770.                     'all' => $collectionAllCount,
  771.                     'to' => 'collection',
  772.                 ];
  773.                 // кешируем на продакшене
  774.                 if (App::isGeneral()) {
  775.                     $this->memcached->add($memcacheName$collectionsfalse, (int)TimeConstant::MINUTE 10);
  776.                 }
  777.             }
  778.         }
  779.         if ($noResponse) {
  780.             $response $collections;
  781.         } else {
  782.             $response $this->ok($collectionstrue);
  783.         }
  784.         return $response;
  785.     }
  786.     /**
  787.      * Страница фильтров под реакт
  788.      * @return JsonResponse|Response|null
  789.      * @throws Exception
  790.      */
  791.     public function pageAdvancedSearchReactAction()
  792.     {
  793.         $data $this->getAdvancedSearchPageData();
  794.         $data['trans'] = [
  795.             'countPluralInterior' => App::transPluralStrToArray($this->translate('filter.res.count.interior')),
  796.             'countPluralArticles' => App::transPluralStrToArray($this->translate('filter.res.count.articles')),
  797.             'countPluralCollection' => App::transPluralStrToArray($this->translate('filter.res.count.collection')),
  798.             'searchHistory' => $this->translate('search_history_advancedsearch'),
  799.             'leftMenuCriteria' => $this->translate('left_menu_criteria'),
  800.             'leftMenuShow' => $this->translate('left_menu_show'),
  801.             'leftAdvancedReset' => $this->translate('left_advanced_reset'),
  802.             'leftAdvancedSearch' => $this->translate('left_advanced_search'),
  803.             'leftMenuSelect' => $this->translate('left_menu_select'),
  804.             'leftMenuCollections' => $this->translate('left_menu_collections'),
  805.             'searchNoResult' => $this->translate('search_no_result'),
  806.             'factoryFirstLi' => $this->translate('factory_first_li'),
  807.             'factoryFirstLiLink' => $this->translate('factory_first_li_link'),
  808.             'factoryFirstLiHelp' => $this->translate('factory_first_li_help'),
  809.             'leftMenuFactories' => $this->translate('left_menu_factories'),
  810.             'filterHideFilters' => $this->translate('filter_hide_filters'),
  811.             'filterShowAllFilters' => $this->translate('filter_show_all_filters'),
  812.             'leftMenuAll' => $this->translate('left_menu_all'),
  813.             'clear' => $this->translate('clear'),
  814.             'filtersTipsWallAndFloor' => $this->translate('filters.tips.wallAndFloor'),
  815.             'hintCollGroupTypeBassein' => $this->translate('hint_coll_group_type_bassein'),
  816.             'hintCollGroupTypeBorder' => $this->translate('hint_coll_group_type_border'),
  817.             'hintCollGroupTypePencil' => $this->translate('hint_coll_group_type_pencil'),
  818.             'hintCollGroupTypeRiser' => $this->translate('hint_coll_group_type_riser'),
  819.             'hintCollGroupTypeStep' => $this->translate('hint_coll_group_type_step'),
  820.             'hintCollGroupTypeTypeCorner' => $this->translate('hint_coll_group_type_type_corner'),
  821.             'hintCollGroupTypeCornerProfile' => $this->translate('hint_coll_group_type_corner_profile'),
  822.             'hintCollGroupTypeCornerElement' => $this->translate('hint_coll_group_type_corner_element'),
  823.             'hintCollGroupTypeBaseboard' => $this->translate('hint_coll_group_type_baseboard'),
  824.             'hintCollGroupTypePanel' => $this->translate('hint_coll_group_type_panel'),
  825.             'hintCollGroupTypeMosaicImitation' => $this->translate('hint_coll_group_type_mosaic_imitation'),
  826.             'hintCollGroupTypeMosaic' => $this->translate('hint_coll_group_type_mosaic'),
  827.             'chooseDropdownTooltip' => $this->translate('choose_dropdown_tooltip'),
  828.         ];
  829.         $data['path'] = App::getRouter()->generate('app_request', ['action' => 'get']);
  830.         $data['map'] = array_values($data['map']);
  831.         foreach ($data['filters'] as $i => $fl) {
  832.             if ($i == 'dimension') {
  833.                 foreach ($fl as $ii => $fll) {
  834.                     $fl[$ii] = array_values($fll);
  835.                 }
  836.             } else {
  837.                 $fl array_values($fl);
  838.             }
  839.             $data['filters'][$i] = $fl;
  840.         }
  841.         $data['history']['actionSave'] = App::getRouter()->generate(
  842.             'app_filters_advanced_search_json',
  843.             ['func' => 'saveHistory']
  844.         );
  845.         $data['history']['actionGet'] = App::getRouter()->generate(
  846.             'app_filters_advanced_search_json',
  847.             ['func' => 'gethistory']
  848.         );
  849.         $data['history']['list'] = [];//$this->getListHistory(); // TODO Удалить если сделаю через  ?func=gethistory или вернуть
  850.         return $this->renderReact('@Web/Filters/React/page.html.twig', [
  851.             'meta' => [
  852.                 'title' => $this->translate('left_advanced_search') . ' · ' App::getParameter('site_name'),
  853.                 'description' => '',
  854.                 'keywords' => '',
  855.             ],
  856.             'initialState' => ['filters' => $data],
  857.         ]);
  858.     }
  859.     /**
  860.      * @return array
  861.      * @throws DBALException
  862.      * @throws Exception
  863.      */
  864.     private function getListHistory()
  865.     {
  866.         $items = [];
  867.         $rows $this->filterHistorySearchRepository->getHistoryForCurUser();
  868.         foreach ($rows as $row) {
  869.             $names = [];
  870.             if ($row['collId']) {
  871.                 if ($nameColl $this->collectionRepository->getNameForFilterHistorySearch($row['collId'])) {
  872.                     $names[] = $nameColl;
  873.                 }
  874.             }
  875.             if ($row['brandId']) {
  876.                 $row['filterIds'][] = $row['brandId'];
  877.             }
  878.             // новый объект для фронта, из которого можно собрать значения группы - фильтр
  879.             $groupedFilter = [];
  880.             if ($row['filterIds']) {
  881.                 $idsParam = [];
  882.                 $fids = [];
  883.                 $namesF = [];
  884.                 foreach ($row['filterIds'] as $fid) {
  885.                     $ids explode('::'$fid);
  886.                     if (!$ids[0]) {
  887.                         continue;
  888.                     }
  889.                     $fids[] = $ids[0];
  890.                     // обработка через :: для запросов по размерам, т.к. ID там идет вида 56564::0.5
  891.                     // где первое - ID, а второе параметр
  892.                     if (count($ids) > 1) {
  893.                         $idsParam[$ids[0]] = $ids[1];
  894.                     }
  895.                 }
  896.                 $filters $this->filterRepository->getForFilterNameHistorySearch($fids);
  897.                 $dimensions = [];
  898.                 if ($filters) {
  899.                     foreach ($filters as $fl) {
  900.                         $name $fl['name'];
  901.                         $groupAltName $fl['groupAltName'];
  902.                         $groupLeftMenu $fl['groupLeftMenu'];
  903.                         // если группа размеров содержит более одного элемента, формируем дополнительный массив $dimensions
  904.                         if ($groupAltName == 'dimension') {
  905.                             if (!empty($idsParam[$fl['id']])) {
  906.                                 $size $idsParam[$fl['id']];
  907.                                 $sizeEd App::trans('left_menu_' $fl['code']);
  908.                                 $type str_replace('2'''$fl['altName']);
  909.                                 $ind $type == $fl['altName'] ? 2;
  910.                                 $dim array_merge($fl, [
  911.                                     'size' => $size,
  912.                                     'sizeEd' => $sizeEd,
  913.                                     'type' => $type,
  914.                                 ]);
  915.                                 $dimensions[$type][$ind] = $dim;
  916.                                 $name null;
  917.                             }
  918.                             // логика для БМов
  919.                         } elseif ($groupAltName == 'bm') {
  920.                             if (!empty($idsParam[$fl['id']])) {
  921.                                 $bmId $idsParam[$fl['id']];
  922.                                 /** @var UserRepository $repoUser */
  923.                                 $repoUser App::getRepository('WebBundle:User');
  924.                                 $name $repoUser->getUserEasyNameById($bmId);
  925.                                 $name "{$fl['name']}{$name}";
  926.                             }
  927.                         } else {
  928.                             if (!isset($groupedFilter[$groupAltName])) {
  929.                                 $groupedFilter[$groupAltName] = [
  930.                                     'name' => App::trans($groupLeftMenu ?: $groupAltName),
  931.                                     'values' => [],
  932.                                 ];
  933.                             }
  934.                             $groupedFilter[$groupAltName]['values'][] = $name;
  935.                         }
  936.                         if ($name) {
  937.                             $namesF[] = $name;
  938.                         }
  939.                     }
  940.                 }
  941.                 if ($dimensions) {
  942.                     $groupedFilter['dimension'] = [
  943.                         'name' => App::trans('left_menu_dimensions'),
  944.                         'values' => [],
  945.                     ];
  946.                     foreach ($dimensions as $type => $dims) {
  947.                         // для создания вывода фильтров единой строкой, а не по отдельности
  948.                         if (count($dims) > 1) {
  949.                             $d1 $dims[1];
  950.                             $d2 $dims[2];
  951.                             $first explode(' '$d1['name']);
  952.                             $first array_shift($first);
  953.                             $name App::trans(
  954.                                 'settings_size_from_to',
  955.                                 null,
  956.                                 ['%size%' => $d1['size'], '%size2%' => $d2['size']]
  957.                             );
  958.                             $name "{$first} {$name} {$d1['sizeEd']}";
  959.                         } else {
  960.                             $d array_shift($dims);
  961.                             $name "{$d['name']} {$d['size']} {$d['sizeEd']}";
  962.                         }
  963.                         $groupedFilter['dimension']['values'][$type] = $name;
  964.                         $namesF[] = $name;
  965.                     }
  966.                 }
  967.                 asort($namesF);
  968.                 $names array_merge($names$namesF);
  969.             }
  970.             $name implode(', '$names);
  971.             $items[] = [
  972.                 'name' => $name,
  973.                 'groupAltName' => $groupedFilter,
  974.                 'getElLink' => App::getRouter()->generate(
  975.                     'app_filters_advanced_search_json',
  976.                     ['func' => 'getLinkHistory''hash' => $row['hash']]
  977.                 ),
  978.                 'delEl' => App::getRouter()->generate(
  979.                     'app_filters_advanced_search_json',
  980.                     ['func' => 'delLinkHistory''hash' => $row['hash']]
  981.                 ),
  982.             ];
  983.         }
  984.         return $items;
  985.     }
  986.     /**
  987.      * Меню для реакта
  988.      * @return JsonResponse|Response|null
  989.      * @throws Exception
  990.      */
  991.     public function ajaxListMenuLite()
  992.     {
  993.         $oLeftService App::getContainer()->get('app.service.leftmenu');
  994.         $output $oLeftService->getDataMenuLite();
  995.         return $this->ok($outputtrue);
  996.     }
  997. }