<?php
namespace WebBundle\Service;
use AdmBundle\Helper\Adm;
use Exception;
use FlexApp\Constant\CatalogConst;
use FlexApp\DTO\BrandResponseDTO;
use FlexApp\DTO\FilterCatalogDTO;
use FlexApp\DTO\FilterGroupCatalogDTO;
use FlexApp\DTO\FilterGroupsCatalogDTO;
use FlexApp\DTO\FilterResponseDTO;
use FlexApp\Service\RedisCachePool;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use WebBundle\Entity\Article;
use WebBundle\Entity\Collection;
use WebBundle\Helper\App;
use WebBundle\Helper\ArrHelper;
use WebBundle\Helper\ConversionHelper;
use WebBundle\Helper\LocaleHelper;
use WebBundle\Helper\RequestHelper;
use WebBundle\Helper\StrHelper;
use WebBundle\Repository\ArticleRepository;
use WebBundle\Repository\CollectionRepository;
use WebBundle\Repository\FilterRepository;
use WebBundle\Repository\LastUrlRepository;
class FiltersService extends ExtendService
{
private string $locale;
private ?int $alsoCollViewedId = null;
private ?bool $isOneFilter = null;
private $urlKeyStr = [];
/** @var array|FilterResponseDTO[] */
private array $curFilters = [];
/** массив id фильтров */
protected $aCurFilterIds = [];
/** не найденные ключи из строки запроса*/
protected array $aBadKeys = [];
/** разделить параметров УРЛ фильтров '&'*/
protected string $separator = CatalogConst::SEPARATOR;
/** Массив для фильтра размеров */
private array $aDimensions = [];
/** Массив для фильтра БМов */
private array $aBMs = [];
/** @var CollectionRepository */
protected $collRepo;
/** @var FilterRepository */
protected $filterRepo;
/** @var ArticleRepository */
protected $articleRepo;
/**
* @throws Exception
*/
public function __construct()
{
$this->locale = App::getCurLocale();
$this->collRepo = App::getRepository('WebBundle:Collection');
$this->filterRepo = App::getRepository('WebBundle:FilterEntity');
$this->articleRepo = App::getRepository('WebBundle:Article');
return $this;
}
/**
* Сортировка , что бы вывод был в том положении, как лайт меню
* @return FilterResponseDTO[]
*/
private function getSortedFiltersLikeInTheLiteMenu()
{
$pattern = [
'brand',
'effect',
'style',
'price',
'color',
'using',
'samples',
];
$arr = [];
foreach ($this->curFilters as $filter) {
$arr[$filter->getGroupAltName()][] = $filter;
}
$res = [];
foreach (array_flip($pattern) as $k => $v) {
if (isset($arr[$k])) {
$res[$k] = $arr[$k];
unset($arr[$k]);
}
}
$af = [];
foreach ($res as $v) {
$af = array_merge($af, $v);
}
foreach ($arr as $v) {
$af = array_merge($af, $v);
}
return $af;
}
/**
* @param array $curFilters
*/
private function setCurFilters(array $curFilters)
{
$this->curFilters = $curFilters;
}
private function margeFilters()
{
$parentMap = $this->filterRepo->getFiltersSubRelation(true);
$result = $this->aCurFilterIds;
foreach ($parentMap as $parentId => $childIds) {
// Проверяем, все ли дочерние ID присутствуют в выбранных фильтрах
$allChildrenPresent = true;
foreach ($childIds as $childId) {
if (!in_array($childId, $result)) {
$allChildrenPresent = false;
break;
}
}
// Если все дети присутствуют, заменяем их на родителя
if ($allChildrenPresent) {
$result = array_diff($result, $childIds); // Удаляем детей
$result[] = $parentId; // Добавляем родителя
}
}
$this->aCurFilterIds = array_values($result);
}
public function isMargeFilters(int $filterId = 0): bool
{
$parentMap = $this->filterRepo->getFiltersSubRelation(true);
if (array_key_exists($filterId, $parentMap)) {
return true;
}
return false;
}
public function unMargeFilters()
{
$parentMap = $this->filterRepo->getFiltersSubRelation(true);
$results = $this->aCurFilterIds;
foreach ($results as $result) {
// Проверяем, все ли дочерние ID присутствуют в выбранных фильтрах
$allChildrenPresent = true;
if (array_key_exists($result, $parentMap)) {
$results = array_merge($parentMap[$result], $results);
//$results = array_diff($results, [$result]);
}
}
$this->aCurFilterIds = array_values($results);
$filters = $this->filterRepo->getSortedByIds($this->aCurFilterIds, $this->locale);
$this->setCurFilters($filters);
}
/**
* Универсальная загрузка данных, написал, но еще не использовал
* @param null $locale
* @return $this
* @throws Exception
*/
private function loadDataUniversal($locale = null)
{
$locale = !$locale ? $this->locale : $locale;
if (!$this->curFilters) {
if (RequestHelper::isAjax()) {
$this->loadBySearchData(RequestHelper::get('data', []));
} else {
$this->loadByUrlKey(App::getRequest()->get('key'), $locale);
}
}
return $this;
}
/**
* Поиск по ID фильтров
* @param $id
* @return $this
*/
public function loadByIds($id, string $lc)
{
$id = is_array($id) ? $id : [$id];
$this->aCurFilterIds = [];
$idsParam = [];
foreach ($id as $item) {
// обработка через :: для запросов по размерам, т.к. ID там идет вида 56564::0.5
// где первое - ID, а второе параметр
$ids = explode('::', $item);
$this->aCurFilterIds[] = $ids[0];
if (count($ids) > 1) {
$idsParam[$ids[0]] = $ids[1];
}
}
$this->margeFilters();
$filters = $this->filterRepo->getSortedByIds($this->aCurFilterIds, $lc);
foreach ($filters as $i => $filter) {
// исключаем не активные фильры, если это не фабрики
if (!$filter->isEnable() and !$filter->getBrand()) {
unset($filters[$i]);
}
}
if (count($idsParam) > 0) {
foreach ($filters as $filter) {
if ($filter->isDimension()) {
$this->aDimensions[$filter->getAltName()] = $idsParam[$filter->getId()];
}
if ($filter->isBM()) {
$this->aBMs[$filter->getAltName()] = $idsParam[$filter->getId()];
}
}
}
$this->setCurFilters($filters);
return $this;
}
public function loadBySearchData($data)
{
if (count($data) > 0) {
$keys = $this->findByBaseInOldCommand($data);
/**
* Получаем ID сортировки из запроса
*/
$sortId = RequestHelper::get('sort');
if (!$sortId) {
$sortId = ArrHelper::get($this->getSearchFilter(), 'getSort.0');
$cookies = App::getRequest()->cookies;
// если нашли ID сортировки, то пишем его в куки
if ($sortId) {
$cookies->set('sort_catalog', $sortId);
}
// получаем итоговую сортировку
$sortId = $cookies->get('sort_catalog');
// если не определна. то ставим поппулярность по умолчанию.
if (!$sortId) {
$cookies->set('sort_catalog', 1);
}
}
$sStyleDesigner = $this->isStyleDesignerAndDesigner($keys);
// пишем ID фильров в массив, из которого и будем формировать всякие URL и прочее
foreach ($keys as $filter) {
if ($filter->isEnable()) {
// выкидываем из фильтров сочетание дизайнера + дизайнерский стиль
if ($sStyleDesigner) {
if ($filter->isDesignerStyle()) {
continue;
}
}
if ($filter->isSort()) {
continue;
}
$this->aCurFilterIds[] = $filter->getId();
// проверяем на наличие группы фильтров с размерами, если нашли, то
// получаем KEY фильтра в текущей локали и по нему ищем значение с троке
// после чего формируем массив вида ["size_x" => "40"]
if ($filter->isDimension()) {
$alias = $filter->getAltName();
foreach ($data as $iAlias => $aVal) {
if ($iAlias == $alias) {
$this->aDimensions[$alias] = $aVal[0];
}
}
}
if ($filter->isBM()) {
$alias = $filter->getAltName();
foreach ($data as $iAlias => $aVal) {
if ($iAlias == $alias) {
$this->aBMs[$alias] = $aVal[0];
}
}
}
}
}
// делаем еще запрос в базу, что бы получить объекты фильтров в нужной сортировке
$this->margeFilters();
$this->setCurFilters($this->filterRepo->getSortedByIds($this->aCurFilterIds, $this->locale));
}
return $this;
}
/**
* Инициируем сам класс, по разбору строки запроса
* с поиском данных в базе фильтров
* @param string|null $key
* @param null $locale
* @return $this
* @throws Exception
*/
public function loadByUrlKey($key, $locale = null)
{
if ($key) {
$keys = $this->urlToArray($key);
$cntDesigner = 0;
if (count($keys) > 0) {
$filters = $this->findByBase($keys, $locale);
// если фильтр AlsoCollViewed , то остальные убираем, на вариант с совпадением по ID
if (!empty($filters[10504])) {
$filters = [$filters[10504]];
$this->alsoCollViewedId = $keys[1];
}
/**
* пишем ID фильров в массив, из которого и будем формировать всякие URL и прочее
*/
foreach ($filters as $filter) {
// исключаем не активные фильры, если это не фабрики
if (!$filter->isEnable() and !$filter->isBrand()) {
continue;
}
// фабрики проверяем дополнительно
if ($oBrand = $filter->getBrand()) {
if (!$oBrand->isShowPage()) {
continue;
}
}
// для дизайнеров ограничиваем вывод - не более одного фильтруется
if ($filter->isDesignerUser()) {
if ($cntDesigner !== 0) {
continue;
}
$cntDesigner++;
}
// если в запросе сортировка, то пишем данные в куки, а сам фильтр исключаем
if ($filter->isSort()) {
$cookies = App::getRequest()->cookies;
if (RequestHelper::get('sort-tmp')) {
// временное изменение сортировки, для ссылки New Arrivals с главной сайта
App::getSession()->set('sort_catalog_tmp', $filter->getSphinxId());
} else {
$cookies->set('sort_catalog', $filter->getSphinxId());
}
continue;
}
$this->aCurFilterIds[] = $filter->getId();
$this->aCurFilterIds = array_unique($this->aCurFilterIds);
// проверяем на наличие группы фильтров с размерами, если нашли, то
// получаем KEY фильтра в текущей локали и по нему ищем значение с троке
// после чего формируем массив вида ["size_x" => "40"]
if ($filter->isDimension()) {
foreach ($keys as $iKey) {
$keyDimensionsAll = $filter->getSlugsLc();
foreach ($keyDimensionsAll as $keyDim) {
if (strval($keyDim) && strpos($iKey, strval($keyDim)) !== false) {
$size = str_replace($keyDim . '-', '', $iKey);
$this->aDimensions[$filter->getAltName()] = $size;
}
}
}
}
if ($filter->isBM()) {
foreach ($keys as $iKey) {
$keyBMs = $filter->getSlug();
if (strval($keyBMs) && strpos($iKey, strval($keyBMs)) !== false) {
$this->aBMs[$filter->getAltName()] = str_replace($keyBMs . '-', '', $iKey);
}
}
}
}
// делаем еще запрос в базу, что бы получить объекты фильтров в нужной сортировке
if ($this->aCurFilterIds) {
$this->margeFilters();
$filters = $this->filterRepo->getSortedByIds($this->aCurFilterIds, $locale);
$this->setCurFilters($filters);
}
}
}
return $this;
}
/**
* @param $altName
* @param string $locale
* @return $this
* @throws Exception
*/
public function loadByAltName($altName, string $locale)
{
$altName = is_array($altName) ? $altName : [$altName];
$filters = $this->filterRepo->getByAltNames($altName);
$fids = [];
foreach ($filters as $filter) {
$fids[] = $filter->getId();
}
$this->loadByIds($fids, $locale);
return $this;
}
/**
* @param null $papam
* @param null $locale
* @return mixed
* @throws Exception
*/
public function getUrl($papam = null, $locale = null)
{
$word = $this->getWord($papam, $locale);
return $this->getFullUrlKeyStr($word, $locale);
}
/**
* Получение KEY для URL строки
* @param null $papam
* @param null $locale
* @return string
* @throws Exception
*/
public function getWord($papam = null, $locale = null)
{
$locale = !$locale ? $this->locale : $locale;
if (!$this->curFilters) {
if (!$papam) {
// если ничего нет, то грузим то, что есть сейчас в зависимости от типа запроса
$this->loadDataUniversal($locale);
} else {
if (is_array($papam)) {
if (!empty($papam[0])) {
// массив индексный
if (intval($papam[0]) === $papam[0]) {
// ищем по ID
$this->loadByIds($papam, $locale);
} else {
// ищем по altName
$this->loadByAltName($papam, $locale);
}
} else {
// массив именованый, значит имеем вид запроса ['getUsings'=>[10,12,58]]
$this->loadBySearchData($papam);
}
} else {
// если значение строковое, то обрабатываем отдельно
if (strripos($papam, $this->separator) !== false) {
// ищем по KEY url
$this->loadByUrlKey($papam, $locale);
} else {
if (intval($papam) === $papam) {
// ищем по ID
$this->loadByIds($papam, $locale);
} else {
// ищем по KEY url
$this->loadByUrlKey($papam, $locale);
if (count($this->curFilters) < 1) {
// ищем по altName
$this->loadByAltName($papam, $locale);
}
}
}
}
}
}
return $this->getUrlKeyStr($locale);
}
/**
* Получаем полную строку вместе с KEY по загруженным ранее данным
* @param null $key
* @param null $locale
* @return mixed
*/
public function getFullUrlKeyStr($key = null, $locale = null)
{
if (!$key) {
$key = $this->getUrlKeyStr($locale);
}
// пока убрал, т.к. через него лезут левые запросы, нам не нужные
// $subkey = RequestHelper::syRequest()->get('subkey');
// if ($subkey) {
// $params['subkey'] = $subkey;
// }
$params['key'] = $key;
if ($locale) {
// если локаль соответсвует текущей, то проверяем на страну доставки
if ($locale == $this->locale) {
$locale = App::getCurLocale(true);
}
$params['_locale'] = $locale;
}
return str_replace('%26', '&', $this->generateUrl('app_catalog', $params));
}
/**
* Получаем строку KEY по загруженным ранее данным
* Получение происходит по ID фильтров из массива
* @param null $locale
* @return string
*/
public function getUrlKeyStr($locale = null)
{
$locale = ($locale != null) ? $locale : $this->locale;
if (empty($this->urlKeyStr[$locale])) {
$str = '';
$isStyleDesignerClear = $this->isStyleDesignerAndDesigner();
foreach ($this->curFilters as $filter) {
// фильтр галвной каталога игнорируем при генерации URL
if (!$filter->isRootCataloge()) {
$slug = $filter->getSlug($locale);
if ($filter->isDimension()) {
$altName = $filter->getAltName();
$size = (float) $this->aDimensions[$altName];
$slug = $slug . '-' . $size;
}
if ($filter->isBM()) {
$altName = $filter->getAltName();
$slug = $slug . '-' . $this->aBMs[$altName];
}
// урл сортировки не выводим
if ($filter->isSort()) {
$slug = '';
}
if ($isStyleDesignerClear and $filter->isDesignerStyle()) {
$slug = '';
}
$str .= $slug . $this->separator;
}
}
$this->urlKeyStr[$locale] = trim($str, $this->separator);
}
return $this->urlKeyStr[$locale];
}
//{"searchFilter":{"getFacturas":["5"]},"searchSort":"2","searchPeriod":null,"locale":"en"}
public function getSearchFilter()
{
$aSearchFilter = [];
foreach ($this->curFilters as $filter) {
$alias = $filter->getSphinxName();
$id = false;
if ($filter->isDimension()) {
// если фильтр размеров, то ставим значение поиска, вместо ID фильтра
// для строки &width-from-12000 это будет 1200
$val = (float) $this->aDimensions[$alias];
} elseif ($filter->isBM()) {
// если фильтр bm, то ставим значение поиска, вместо ID фильтра
// для строки &bm-123123 это будет 123123
$val = $this->aBMs[$alias];
} elseif ($filter->isTop()) {
// если фильтр top, то ставим значение subkey, вместо ID фильтра
$val = RequestHelper::get('subkey');
// $val = $filter->getId();
} elseif ($filter->isDesignerUser()) {
// пока для дизайнеров делаем параметром их имя
$val = $filter->getNameSingle();
$id = $filter->getId();
} else {
$val = $filter->getSphinxId();
}
if ($id != false) {
$aSearchFilter[$alias][] = "{$id}";
}
$aSearchFilter[$alias][] = "{$val}";
}
return (count($aSearchFilter) > 0) ? $aSearchFilter : null;
}
/**
* @param array $data
* @return array|FilterResponseDTO[]
* @throws \Doctrine\DBAL\Driver\Exception
* @throws \Doctrine\DBAL\Exception
*/
private function findByBaseInOldCommand(array $data)
{
$aKeysNew = [];
if (count($data) > 0) {
$filterRepo = $this->filterRepo;
// первый проход поиска, ищем по колному кею
foreach ($data as $old_command => $ids) {
// исключаем запросы с коллекциями
if ($old_command == 'collection') {
continue;
}
if ($old_command == 'factory') {
if (count($ids) > 1) {
unset($ids[1]);
}
} elseif ($old_command == 'getAlsoCollViewed') {
$ids = [0];
}
if ($old_command == 'collection') {
continue;
}
if ($old_command == 'getDesigner') {
$filters = $filterRepo->getSortedByIds($ids, $this->locale);
} else {
if (!is_array($ids)) {
$ids = [$ids];
}
$filters = $filterRepo->getByOldCommand($ids, $old_command, $this->locale);
}
if ($filters) {
unset($data[$old_command]);
foreach ($filters as $filter) {
$aKeysNew[$filter->getId()] = $filter;
}
} else {
$filters = $filterRepo->getFilterDTOByAltName($old_command, $this->locale);
if (count($filters) == 1) {
$filter = $filters[0];
unset($data[$old_command]);
$aKeysNew[$filter->getId()] = $filter;
}
}
}
}
// если в массиве еще остались значения , то пишем значения в массив в бед кеями
// что с ним потом делать пока не знаю, но что то делать будет надо
if (count($data) > 0) {
foreach ($data as $key) {
$this->aBadKeys[] = $key;
}
}
return $aKeysNew;
}
/**
* Ищем значения в базе по имени фильтра
* @param array $aKeys
* @param string|null $locale
* @return array|FilterResponseDTO[]
* @throws \Doctrine\DBAL\Driver\Exception
* @throws \Doctrine\DBAL\Exception
*/
private function findByBase(array $aKeys, $locale = null)
{
$aKeysNew = [];
$repoFilter = $this->filterRepo;
if (count($aKeys) > 0) {
$isSort = false;
// первый проход поиска, ищем по полному кею
foreach ($aKeys as $i => $key) {
// доп логика для фильтрации БМов
if (preg_match("/^bm-(\d*)$/", $key)) {
$key = 'bm';
}
$filter = $repoFilter->getByKeyUrl($key, $locale);
// если фильтр не нашли, то проходимся дополнительно разложив кей на части
if (!$filter) {
$dopKeys = $this->buldExplodeKeys($key);
foreach ($dopKeys as $dkey) {
$filter = $repoFilter->getByKeyUrl($dkey, $locale);
if ($filter) {
break;
}
}
if (!$filter) {
foreach ($dopKeys as $dkey) {
$filter = $repoFilter->getByKeyUrlIsLike($dkey, $locale);
if ($filter) {
break;
}
}
}
}
if ($filter) {
// проверка на включенный фильтр
// фильтр может быть отключен, если это фабрика. Фабрику выводим в любом случае
if ($filter->isEnable() or $filter->isBrand()) {
// проверяем, что бы сортировка была только одна
if ($filter->isSort() && !$isSort) {
$isSort = true;
}
unset($aKeys[$i]);
$aKeysNew[$filter->getId()] = $filter;
}
}
}
// если в массиве еще остались значения , то дополнительно проверяем на наличие редиректов
if (count($aKeys) > 0) {
/** @var $repoLastUrl LastUrlRepository */
$repoLastUrl = App::getRepository('WebBundle:LastUrlEntity');
foreach ($aKeys as $i => $key) {
if ($keyTmp = $repoLastUrl->getActualSlugFiflter($key, $locale)) {
if ($filter = $this->filterRepo->getByKeyUrl($keyTmp, $locale)) {
unset($aKeys[$i]);
$aKeysNew[$filter->getId()] = $filter;
}
}
}
}
}
// если в массиве еще остались значения , то пишем значения в массив в бед кеями
// что с ним потом делать пока не знаю, но что то делать будет надо
if (count($aKeys) > 0) {
foreach ($aKeys as $key) {
$this->aBadKeys[] = $key;
}
}
return $aKeysNew;
}
/**
* @return array|FilterResponseDTO[]
*/
public function getCurFilters()
{
return $this->curFilters;
}
/**
* @return array
*/
public function getDimensions()
{
return $this->aDimensions;
}
/**
* @param mixed $aDimensions
*/
public function setDimensions($aDimensions)
{
$this->aDimensions = $aDimensions;
}
/**
* @return array
*/
public function getBMs()
{
return $this->aBMs;
}
/**
* @param mixed $aBMs
*/
public function setBMs($aBMs)
{
$this->aBMs = $aBMs;
}
/**
* Формируем многомерный массив из строки запроса с фильтрами
* @param $key
* @return array
*/
private function urlToArray($key)
{
$key = StrHelper::toLower($key);
$key = trim(urldecode($key));
$key = $key ? explode($this->separator, $key) : [];
return array_diff($key, ['', ' ', null, false]);
}
/**
* Получаем объект фабрики, если таковая есть в фильтрах
* @return null|BrandResponseDTO
*/
public function getCurBrand()
{
foreach ($this->getCurFilters() as $curFilter) {
if ($brand = $curFilter->getBrand()) {
return $brand;
}
}
return null;
}
/**
* Определяем сколько фильтров примененно, без учета сортировки
* @return bool
*/
public function isOneFilter()
{
if ($this->isOneFilter === null) {
$aCurFilters = $this->getCurFilters();
$countFilters = count($aCurFilters);
if ($countFilters > 1) {
foreach ($aCurFilters as $filter) {
if ($filter->isSort()) {
$countFilters--;
} else {
if (!$filter->isEnable()) {
$countFilters--;
}
}
}
}
$this->isOneFilter = ($countFilters == 1);
}
return $this->isOneFilter;
}
/**
* Проверка на тестовую фабрику
* @return bool
*/
public function isTestingBrand()
{
foreach ($this->curFilters as $filter) {
if ($filter->getAltName() == 'testing_factory') {
return true;
}
}
return false;
}
/**
* Проверка на принадлежность к топу
* @return bool
*/
public function isTopFilter()
{
foreach ($this->curFilters as $filter) {
if ($filter->isTop() && !$filter->isAlsoCollViewed()) {
return true;
}
}
return false;
}
/**
* Проверка на сочетание двых выбранных фильтров дизайнерский стиль + любой дизайнер
* @param array $filters
* @return bool
*/
public function isStyleDesignerAndDesigner(array $filters = [])
{
$filters = $filters ?: $this->curFilters;
if (count($filters) == 2) {
$isDesigner = $this->isExistDesigner($filters);
if ($isDesigner) {
foreach ($filters as $filter) {
if ($filter->isDesignerStyle()) {
return true;
}
}
}
}
return false;
}
public function isExistDesigner(array $filters = []): bool
{
$filters = $filters ?: $this->curFilters;
foreach ($filters as $filter) {
if ($filter->isDesignerUser()) {
return true;
}
}
return false;
}
/**
* Получаем вильтр сортировки, если таковой был в запросе
* @return null|FilterResponseDTO
*/
public function getSortFilter()
{
foreach ($this->getCurFilters() as $curFilter) {
if ($curFilter->isSort()) {
return $curFilter;
}
}
return null;
}
/**
* @param FilterGroupsCatalogDTO $filterGroups
* @param bool $isShot
* @param bool $isLinkToFilter
* @param bool $isLinkEdit
* @return string
*/
public function buldFiltersToStr(
FilterGroupsCatalogDTO $filterGroups,
bool $isShot = false,
bool $isLinkToFilter = false,
bool $isLinkEdit = true
) {
if ($this->alsoCollViewedId) {
return $filterGroups->getFirstGroup()->getFirstFilter()->getTitle();
}
$and = $this->translate('joinder_and', $this->locale);
$ceramicTilesName = $this->translate('catalog_ceramic_tiles', $this->locale);
$sTtitle2 = "$ceramicTilesName ";
$isShowLinkStyleDesigner = $isLinkToFilter;
if ($this->isOneFilter()) {
$sTtitle2 = '';
$isLinkToFilter = false;
$isShowLinkStyleDesigner = $this->isExistDesigner();
}
if ($this->isOneFilter() && $this->getCurBrand()) {
$isLinkEdit = false;
}
// type (вид изделия)
if ($typeGroup = $filterGroups->getProductType()) {
$sTtitle2 = $this->modifyFirstFilter($typeGroup);
} else {
// material
if ($materialGroup = $filterGroups->getMaterial()) {
$sTtitle2 = $this->modifyFirstFilter($materialGroup);
}
}
if ($isShot) {
$sTtitle2 = '';
}
$groups = $filterGroups->getGroups();
$maxCntFilters = 0;
foreach ($groups as $group) {
$cnt = count($group->getListFilters());
if ($cnt > $maxCntFilters) {
$maxCntFilters = $cnt;
}
}
if ($maxCntFilters > 2) {
$separatorGroup = ';';
$separatorFilter = ',';
} else {
$separatorGroup = ',';
$separatorFilter = $and;
}
$cntGroup = count($groups);
$isDotBefore = false;
foreach ($groups as $i => $group) {
$groupName = $group->getGroupName();
$altName = $group->getGroupAltName();
if ($i == 0 || $isDotBefore) {
$groupName = StrHelper::ucFirstOnly($groupName);
} else {
$groupName = StrHelper::toLower($groupName);
}
if ($altName == 'dimension' || $altName == 'rewards' || $altName == 'samples') {
$groupName = '';
} elseif ($altName == 'bm') {
$groupName = StrHelper::toUpper($groupName) . ': ';
} elseif ($altName == 'resistance_abrasion') {
$groupName = StrHelper::toUpper($groupName);
}
if ($this->isOneFilter()) {
$groupName = '';
}
$groupName = $groupName ? "{$groupName} " : "";
$name_ = '';
$listFilters = $group->getListFilters();
$nameCnt = count($listFilters);
foreach ($listFilters as $ii => $filter) {
$pref = '';
if ($ii > 0) {
$pref = $separatorFilter ? "{$separatorFilter} " : ' ';
// для последнего ставим
if ($nameCnt - 1 == $ii) {
$pref = " {$and} ";
}
// для размеров убираем разделители между двумя одинаковами типами
if ($altName == 'dimension') {
if (!empty($listFilters[$ii - 1])) {
$keyPrew = $listFilters[$ii - 1]->getKey();
if ($filter->getKey() == $keyPrew . '2') {
$pref = ' ';
}
}
}
}
$elFilterName = $filter->getTitle() ?? '';
if (!$groupName) {
if (($i == 0 && $ii == 0) || $isDotBefore) {
$elFilterName = StrHelper::ucFirstOnly($elFilterName);
} else {
$elFilterName = StrHelper::toLower($elFilterName);
}
}
$isDotBefore = preg_match('/\.\s?<?/', strip_tags($elFilterName));
if ($isLinkToFilter) {
$elFilterName = $this->buildElemetFilter($elFilterName, $filter->getSlug());
} elseif ($isShowLinkStyleDesigner) {
$key = $this->filterRepo->getKeyDesignerStyle();
$link = $this->generateUrl('app_catalog', ['key' => $key]);
$pref = $this->translate('link_on_designer', null, ['%href%' => $link]) . ' ';
$elFilterName = $filter->getNameMany();
}
if ($isLinkEdit) {
$elFilterName = $this->addElemetFilterLinkEdit($filter->getId(), $filter->getKey(), $elFilterName);
}
if ($this->isMargeFilters($filter->getId())) {
$sub = $this->filterRepo->getArrListSubForMenu($filter->getId(), $this->locale);
$all_sub = [];
foreach ($sub as $subItem) {
$all_sub[] = $this->buildElemetFilter($subItem['pageNameMenu'], $subItem['url']);
}
if ($isLinkToFilter) {
$elFilterName .= ' (' . implode(', ', $all_sub) . ')';
} else {
$elFilterName .= ': ' . implode(', ', $all_sub);
}
}
$name_ .= "{$pref}{$elFilterName}";
}
// фикс случайных нескольких пробелов на всякий случай
$name_ = preg_replace(['# {2,}#umi'], ' ', $name_);
if ($i == $cntGroup - 1) {
$sepGroup = '';
} else {
$sepGroup = "{$separatorGroup} ";
}
// если нашли вконце точку, то разделитель убираем
if ($isDotBefore) {
$sepGroup = '';
}
if ($isLinkToFilter) {
$sTtitle2 .= "<span>{$groupName}{$name_}{$sepGroup}</span>";
} else {
$sTtitle2 .= "{$groupName}{$name_}{$sepGroup}";
}
}
return $sTtitle2;
}
/**
* @param string $name
* @param string $slug
* @return string
*/
private function buildElemetFilter(string $name, string $slug): string
{
$link = $this->generateUrl('app_catalog', ['key' => $slug]);
return "<a class=\"el-filter\" href=\"{$link}\">{$name}</a>";
}
/**
* @param int $id
* @param string $key
* @param string $nameFilterStr
* @return string
*/
private function addElemetFilterLinkEdit(int $id, string $key, string $nameFilterStr): string
{
$linkEdit = $key == 'factory' ? Adm::linkEdit('adm.brand.edit', $id) : Adm::linkEdit('adm.filter.edit', $id);
$linkEdit = $linkEdit ? "<sup> <a class=\"link-edit\" href=\"$linkEdit\" target=\"_blank\">edit</a></sup>" : null;
return $linkEdit ? "{$nameFilterStr}{$linkEdit}" : $nameFilterStr;
}
/**
* @return FilterGroupsCatalogDTO
* @throws Exception
*/
public function buildFilterGroupsDTO(): FilterGroupsCatalogDTO
{
$aCurFilters = $this->getSortedFiltersLikeInTheLiteMenu();
$isOneFilter = $this->isOneFilter();
//$isDesignerUser = $this->filterService()->isDesignerUser();
$filterGroups = new FilterGroupsCatalogDTO();
// если фильтр размеров есть, то создаем массив с количеством значений по группам, для будущего формирования
$aDimensionsCnt = [];
if ($aDimensions = $this->getDimensions()) {
foreach ($aDimensions as $key => $val) {
$key = str_replace('2', '', $key);
if (!empty($aDimensionsCnt[$key])) {
$aDimensionsCnt[$key] += 1;
} else {
$aDimensionsCnt[$key] = 1;
}
}
}
foreach ($aCurFilters as $filter) {
$groupAltName = $filter->getGroupAltName();
if (!$filterGroup = $filterGroups->getGroup($groupAltName)) {
$filterGroup = new FilterGroupCatalogDTO($filter->getGroupId(), $filter->getGroupName(), $groupAltName);
$filterGroups->addGroup($filterGroup);
}
$nameSingle = $filter->getNameSingle();
$nameMany = $filter->getNameMany();
$dopValue = null;
$slug = $filter->getSlug();
if ($filter->isDimension()) {
$dopValue = $this->aDimensions[$filter->getSphinxName()];
$slug = "{$slug}-{$dopValue}";
} elseif ($groupAltName == 'bm') {
$dopValue = $this->aBMs['bm'];
$slug = "{$slug}-{$dopValue}";
} elseif ($filter->isAlsoCollViewed()) {
$dopValue = $this->alsoCollViewedId;
}
$filterGroup->addListFilters(
new FilterCatalogDTO(
$filter->getId(),
$filter->getSphinxName(),
$filter->getSphinxId(),
$filter->getCode(),
$filter->getAltName(),
$slug,
$nameSingle,
$nameMany,
$dopValue,
)
);
}
foreach ($filterGroups->getGroups() as $groupDto) {
$groupAltName = $groupDto->getGroupAltName();
foreach ($groupDto->getListFilters() as $filterDto) {
$title = $isOneFilter ? $filterDto->getNameSingle() : $filterDto->getNameMany();
$filterDto->setTitle($title);
switch ($groupAltName) {
case 'antislip_coff':
$groupName = $this->translate('antislip_full_name', $this->locale, ['%d%' => '']);
$groupDto->setGroupName($groupName);
break;
case 'resistance_abrasion':
// $title = trim(str_replace('PEI', '', $title));
$filterDto->setTitle($title);
break;
case 'price':
$title = LocaleHelper::getNamePriceFilter($title, $this->locale);
$title = LocaleHelper::getNamePriceFilterWithCurrency($title);
$filterDto->setTitle($title);
break;
case 'bm':
$repoUser = App::getRepository('WebBundle:User');
$title = $repoUser->getUserEasyNameById($filterDto->getDopValue());
$filterDto->setTitle($title);
break;
case 'top':
if ($filterDto->getKey() == 'getAlsoCollViewed') {
if ($collAlsoId = $filterDto->getDopValue()) {
/** @var Collection $coll */
$coll = $this->collRepo->getCollForAlsoById($collAlsoId);
if (null === $coll) {
throw new NotFoundHttpException();
}
$factory = $coll->getFactory();
$collName = $coll->getName();
$brandName = $factory->getName();
if ($coll->getStatus() == 1 || $factory->getStatus() == 1) {
$collUrl = $this->generateUrl(
'app_collection',
['factoryUrl' => $factory->getUrl(), 'collectionUrl' => $coll->getUrl()]
);
$collName = "<a href=\"$collUrl\" title=\"{$coll->getName()}\">{$coll->getName()}</a>";
$brandUrl = $this->generateUrl('app_catalog', ['key' => $factory->getUrl()]);
$brandName = "<a href=\"$brandUrl\" title=\"$brandName\">$brandName</a>";
}
} else {
$collName = $this->translate('collection');
$brandName = $this->translate('brand');
}
$title = str_replace('%collection%', $collName, $title);
if (stripos($title, '%brand%') !== false) {
$title = str_replace('%brand%', $brandName, $title);
}
$filterDto->setTitle($title);
}
break;
case 'dimension':
$sizeVal = $aDimensions[$filterDto->getKey()];
$sizeEd = $this->translate("left_menu_{$filterDto->getCode()}", $this->locale);
$dimKey = str_replace('2', '', $filterDto->getKey());
// если группа размеров содержит более одного элемента, формируем дополнительный массив
if ($aDimensionsCnt[$dimKey] > 1) {
$aSizeType = explode(' ', $title);
$firstTitle = array_shift($aSizeType);
$pattern = $this->translate('settings_size_from_to', $this->locale);
if ($pattern and $pattern != 'settings_size_from_to') {
if (preg_match('/(.*%size%)\s(.*%size2%)/', $pattern, $out)) {
if (preg_match('/.*2$/', $filterDto->getKey())) {
$title = str_replace('%size2%', $sizeVal, $out[2]);
$title = "{$title} {$sizeEd}";
} else {
$title = str_replace('%size%', $sizeVal, "{$out[1]} ");
$title = "{$firstTitle} {$title}";
}
}
}
} else {
$title = "{$title} {$sizeVal} {$sizeEd}";
}
$filterDto->setTitle($title);
break;
}
}
}
return $filterGroups;
}
/**
* разбиваем строку кея на полстроки для дополнительного поиска. например для размеров или БМа
* @param string $key
* @return array
*/
private function buldExplodeKeys(string $key)
{
$res = [];
$aKey = explode('-', $key);
if (count($aKey) > 2) {
// создаем копию массива и удаляем последний элемент
$aKeyTmp = $aKey;
array_pop($aKeyTmp);
$res[] = implode('-', $aKeyTmp);
// создаем копию массива и удаляем последний элемент
$aKeyTmp = $aKey;
array_shift($aKeyTmp);
$res[] = implode('-', $aKeyTmp);
}
foreach ($aKey as $k) {
if (!is_numeric($k) && mb_strlen($k, 'utf-8') > 2) {
$res[] = $k;
}
}
// в конец добавим полный кей, для LIKE поиска
$res[] = $key;
return array_unique($res);
}
/**
* Правим фильтр, который должен быть первым и определяющим для страницы
* @param FilterGroupCatalogDTO $filterGroup
* @return string
*/
private function modifyFirstFilter(FilterGroupCatalogDTO $filterGroup): string
{
$tilesName = $this->translate('footer_tile', $this->locale);
if (count($filterGroup->getListFilters()) > 1) {
return "{$tilesName}. ";
}
// ставим отдельным предложением в начало
$afterDot = $this->isOneFilter() ? '' : '. ';
$firstFilter = $filterGroup->getFirstFilter();
$firstFilter->setTitle("{$firstFilter->getNameSingle()}{$afterDot}");
$filterGroup->setGroupName('');
return '';
}
private function getSizeInCalc(array $input): array
{
$items = array_keys($input);
sort($items);
$items = array_map(function ($num) {
$num = (int)$num;
$num = $num / 100;
$n = round($num, 2, PHP_ROUND_HALF_DOWN);
return ($n == 0.0) ? null : $n;
}, $items);
// Удаляем null'ы
$items = array_filter($items, function ($val) {
return $val !== null;
});
return array_unique($items);
}
/**
* Перенести из контроллера в сервис поиска, а лучше в сервис фильтров
*
* @return array|string
* @throws Exception
*/
public function getFiltersList()
{
$lc = App::getCurLocale();
$cc = App::getCurCountry();
$lcFull = $lc == $cc ? $lc : "{$lc}_$cc";
$cur = LocaleHelper::getCurrency();
$msr = LocaleHelper::getUserMeasure();
$memcache = App::getMemcache();
$redisCachePool = App::getContainer()->get(RedisCachePool::class)->getPool();
// $memcacheKey = "collection_filter_factory_search_page_list_$lcFull.$cur.$msr";
// $filters = $memcache->get($memcacheKey);
//
// if ($filters) {
// return $filters; //todo тест без кеша
// }
$filters = [];
$filtersTmp = $this->filterRepo->getArrListForFilterMenuNew($lc);
//приписываем новые значения Просьба:Подвисает отбор по отзывам на рабочем сайте
$memKeyCalc = 'all_calculate_' . $cc . '_' . $msr;
$allCalculateItem = $redisCachePool->getItem($memKeyCalc);
if ($allCalculateItem->isHit()) {
$allCalculate = $allCalculateItem->get();
$sizesX = $this->getSizeInCalc($allCalculate['size_x']);
$sizesY = $this->getSizeInCalc($allCalculate['size_y']);
$thicks = $this->getSizeInCalc($allCalculate['tolsch']);
$filtersTmp = $this->setNewCount($filtersTmp, $allCalculateItem->get());
} else {
$allCalculate = false;
$thicks = $this->articleRepo->getSizeValues('thick');
$sizesY = $this->articleRepo->getSizeValues('sizeY');
$sizesX = $this->articleRepo->getSizeValues('sizeX');
}
// проверяем наличие вложенных фильтров
$sub = [];
$pids = array_filter($filtersTmp, function ($r) {
return $r['pid'] != null;
});
if ($pids) {
foreach ($pids as $id => $r) {
unset($filtersTmp[$id]);
$sub[$r['pid']][$id] = $r;
}
}
foreach ($filtersTmp as $filter) {
$fid = $filter['id'];
$groupAltName = $filter['group']['altName'];
// вставляем вложенные фильтры, если есть такие
if (!empty($sub[$fid])) {
$filter['sub'] = $sub[$fid];
}
$filters[$groupAltName][$fid] = $filter;
// формируем фильтры для БМов
if ($groupAltName == 'bm') {
$filters['bm'] = [];
$aUsers = $this->collRepo->getListActiveBM();
foreach ($aUsers as $id => $name) {
$filters['bm'][$id] = [
'fid' => $fid,
'id' => $id,
'name' => $name,
'altName' => $id,
'selected' => [],
'group' => $filter['group'],
'count' => 0,
];
}
}
// формируем фильтры для размеров
if ($groupAltName == 'dimension') {
// проверяем на Дюймы и если что конвертируем
if (LocaleHelper::measureGb()) {
$measure = '″';
if (StrHelper::isInStr($filter['altName'], 'tolsch')) {
$koff = ConversionHelper::convertInch(0.1, ConversionHelper::MM, 5); //mm так там выходит 0.00394
} else {
$koff = ConversionHelper::convertInch(1.0, ConversionHelper::CM); //cm
}
} else {
if (StrHelper::isInStr($filter['altName'], 'tolsch')) {
$measure = 'left_menu_mm';
} else {
$measure = 'left_menu_cm';
}
$measure = $this->translate($measure);
$koff = 1;
}
if (!StrHelper::isInStr($filter['altName'], '2')) {
$filters[$groupAltName][$fid]['subGroup'] = [
'name' => $this->translate('dimensions_from'),
'altName' => 'from',
'suff' => '',
'measure' => '',
];
} else {
$filters[$groupAltName][$fid]['subGroup'] = [
'name' => $this->translate('dimensions_to'),
'altName' => 'to',
'suff' => '2',
'measure' => $measure,
];
}
/** @var $item Article */
if (StrHelper::isInStr($filter['altName'], 'tolsch')) {
$subGroupName = $this->translate('left_menu_thickness');
$filters[$groupAltName][$fid]['name'] = $subGroupName;
$sizes = [];
foreach ($thicks as $s) {
$sizes[] = [
'name' => $s * $koff,
'val' => $s,
];
}
if (!StrHelper::isInStr($filter['altName'], '2')) {
array_pop($sizes);
}
$filters[$groupAltName][$fid]['sizes'] = $sizes;
$filters[$groupAltName][$subGroupName][$fid] = $filters[$groupAltName][$fid];
}
if (StrHelper::isInStr($filter['altName'], 'size_y')) {
$subGroupName = $this->translate('left_menu_height');
$filters[$groupAltName][$fid]['name'] = $subGroupName;
foreach ($sizesY as $s) {
$filters[$groupAltName][$fid]['sizes'][] = [
'name' => $s * $koff,
'val' => $s,
];
}
$filters[$groupAltName][$subGroupName][$fid] = $filters[$groupAltName][$fid];
}
if (StrHelper::isInStr($filter['altName'], 'size_x')) {
$subGroupName = $this->translate('left_menu_width');
$filters[$groupAltName][$fid]['name'] = $subGroupName;
foreach ($sizesX as $s) {
$filters[$groupAltName][$fid]['sizes'][] = [
'name' => $s * $koff,
'val' => $s,
];
}
$filters[$groupAltName][$subGroupName][$fid] = $filters[$groupAltName][$fid];
}
unset($filters[$groupAltName][$fid]);
}
}
// переносим пачку противоскольжения в groupList фильтра Противоскользящая
//todo не нужно если перенести противоскользыщие фильтры в подгруппу 10146 в группу 401
// UPDATE `filters` SET `parent_id` = '10146', `group_id` = 401 WHERE group_id=422
// логика обработки в sphinx
// if (!empty($filters['surface']) && !empty($filters['surface']['10146'])) {
// if (!empty($filters['antislip_coff'])) {
// $filters['surface']['10146']['groupList'] = $filters['antislip_coff'];
// unset($filters['antislip_coff']);
// }
// }
$paramGetCollections = [
'order' => 'c.name, c.publishDate DESC, c.id',
'onlyCF' => true,
'leftCollectionCount' => true,
'locale' => App::getCurLocale(),
];
$aColls = $this->collRepo->getCollections($paramGetCollections);
//получаем фабрики, которые скрытые в стране
$issetCollectionFactory = $allCalculate ? array_keys($allCalculate['factory']) : [];
$issetCollection = $allCalculate ? array_keys($allCalculate['c_id']) : [];
//$all_coll=[];
// foreach ($aColls as $coll){
// $all_coll[]=$coll['id'];
// }
// $result = array_diff($all_coll, $issetCollection);
//
// App::debugExit($paramGetCollections,$result,count($issetCollection),count($all_coll), $issetCollection,$all_coll);
foreach ($aColls as $coll) {
if (empty($coll['brandUrl']) || empty($coll['url'])) {
continue;
}
if (!empty($issetCollectionFactory) && !in_array($coll['id'], $issetCollection)) {
//App::debugExit($coll, $issetCollection);
continue;
}
$filters['collection'][$coll['id']] = [
'id' => $coll['id'],
'url' => $coll['url'],
'name' => $coll['nameFull'],
'altName' => $coll['alternateName'],
'selected' => [],
'brand' => [
'id' => $coll['brandId'],
'url' => $coll['brandUrl'],
],
'dataLink' => $this->generateUrl(
'app_collection',
['factoryUrl' => $coll['brandUrl'], 'collectionUrl' => $coll['url']]
),
'group' => [
'name' => App::getTranslator()->trans('left_menu_collections'),
'altName' => 'collections',
],
];
if (!empty($filters['brand'][$coll['brandId']])) {
if (!in_array($coll['id'], $filters['brand'][$coll['brandId']]['collection'])) {
$filters['brand'][$coll['brandId']]['collection'][] = $coll['id'];
}
}
}
//удаляем бренды которые скрыты
if (!empty($issetCollectionFactory)) {
//Удаляем фабрики если есть скрытые бренды
$old = count($filters['brand']);
foreach ($filters['brand'] as $brKey => $brV) {
if (!in_array($brV['id'], $issetCollectionFactory)) {
unset($filters['brand'][$brKey]);
}
}
}
// сортируем фабрики по имени
if (!empty($filters['brand'])) {
$brandSort = array_column($filters['brand'], 'name');
array_multisort($brandSort, SORT_ASC, $filters['brand']);
}
// сортируем выставки по ID
if (!empty($filters['exhibition'])) {
$exhSort = array_column($filters['exhibition'], 'id');
array_multisort($exhSort, SORT_DESC, $filters['exhibition']);
}
// кешируем на продакшене
// if (App::isGeneral()) {
// $memcache->add($memcacheKey, $filters, MEMCACHE_COMPRESSED, (int)(TimeConstant::HOUR * 12));
// }
return $filters;
}
public function setNewCount(array $filters, array $allCalculate): array
{
//меняем логику перебираем фильтры и ищем новое значения иначе 0
foreach ($filters as $filter) {
if (!empty($allCalculate[$filter['oldCommand']][$filter['id']])) {
$filters[$filter['id']]['count'] = $allCalculate[$filter['oldCommand']][$filter['id']];
}
// else {
// // unset($filters[$filter['id']]);//=0; todo не все
// }
}
// foreach ($allCalculate as $group) {
// foreach ($group as $id => $count) {
// if (!empty($filters[$id])) {
// $filters[$id]['count'] = $count;
// }
// }
// }
return $filters;
}
}