src/WebBundle/Service/CollectionSettingsService.php line 404

Open in your IDE?
  1. <?php
  2. namespace WebBundle\Service;
  3. use Doctrine\ORM\OptimisticLockException;
  4. use Doctrine\ORM\ORMException;
  5. use Exception;
  6. use WebBundle\Helper\App;
  7. use WebBundle\Helper\FilterHelper;
  8. use WebBundle\Helper\StrHelper;
  9. use WebBundle\Repository\ArticleRepository;
  10. use WebBundle\Repository\CollectionRepository;
  11. use WebBundle\Repository\FilterRepository;
  12. use WebBundle\Repository\InteriorRepository;
  13. class CollectionSettingsService extends ExtendService
  14. {
  15.     /** @required */
  16.     public SliderService $sliderService;
  17.     /** @var FilterRepository */
  18.     protected $repoFilter;
  19.     /** @var ArticleRepository */
  20.     protected $repoArticle;
  21.     /** @var InteriorRepository */
  22.     protected $repoInterior;
  23.     /** @var CollectionRepository */
  24.     protected $repoColl;
  25.     /**
  26.      * CollectionSettingsService constructor.
  27.      * @throws Exception
  28.      */
  29.     public function __construct()
  30.     {
  31.         $this->repoFilter App::getRepository('WebBundle:FilterEntity');
  32.         $this->repoArticle App::getRepository('WebBundle:Article');
  33.         $this->repoInterior App::getRepository('WebBundle:Interior');
  34.         $this->repoColl App::getRepository('WebBundle:Collection');
  35.         return $this;
  36.     }
  37.     private function parseAttr($attr)
  38.     {
  39.         $arr = [];
  40.         $keys = ['applies''styles''style''textures''pei''type''material''using''surface''edgeType''sliding''offShade'];
  41.         if (isset($attr['expressSample'])) {
  42.             if ($attr['expressSample']) {
  43.                 $arr['articleQuickSample'] = [
  44.                     'id' => 1,
  45.                     'name' => 'Экспресс образцы',
  46.                     'alias' => 'articleQuickSample',
  47.                     'key' => 'expressSample',
  48.                 ];
  49.             }
  50.         }
  51.         foreach ($keys as $key) {
  52.             if (!empty($attr[$key])) {
  53.                 $item $attr[$key];
  54.                 if (!empty($item['hide'])) {
  55.                     // скрытые фильтры не показываем в свойствах коллекции
  56.                     continue;
  57.                 }
  58.                 if (!empty($item['alias'])) {
  59.                     $item['key'] = $key;
  60.                     $arr[$item['alias']] = $item;
  61.                 } else {
  62.                     if (is_array($item)) {
  63.                         foreach ($item as $row) {
  64.                             if (empty($row['alias'])) {
  65.                                 continue;
  66.                             }
  67.                             $row['key'] = $key;
  68.                             $arr[$row['alias']] = $row;
  69.                         }
  70.                     } else {
  71.                         $i FilterHelper::normaliseAltName($item);
  72.                         $arr[$i] = $item;
  73.                     }
  74.                 }
  75.             }
  76.         }
  77.         return $arr;
  78.     }
  79.     private function parseAttrDesigner($authors)
  80.     {
  81.         $arr = [];
  82.         foreach ($authors as $author) {
  83.             $key 'designer_' FilterHelper::normaliseAltName($author);
  84.             $arr[$key] = [
  85.                 'name'  => $author,
  86.                 'alias' => 'designer',
  87.             ];
  88.         }
  89.         return $arr;
  90.     }
  91.     private function getFilterIds($rows$cid$status)
  92.     {
  93.         $ids = [];
  94.         foreach ($rows as $k => $row) {
  95.             $f null;
  96.             if (preg_match('/pei_(.*)/ui'$k)) {
  97.                 $alias 'left_menu_' strtolower($k);
  98.                 $f $this->repoFilter->getFilterIdForSettingsCollectionByLeft($alias);
  99.             } elseif (!empty($row['alias'])) {
  100.                 if ($row['alias'] == 'designer') {
  101.                     $f $this->repoFilter->getFilterIdForSettingsColletionByIdDesigner($row['id']);
  102.                 } else {
  103.                     $f $this->repoFilter->getFilterIdForSettingsCollectionByLeft($row['alias']);
  104.                 }
  105.             }
  106.             if ($f) {
  107.                 $ids[$f['name']] = $f;
  108.             } else {
  109.                 if ($status != 2) {
  110.                     throw new Exception("Не нашли");
  111. //                    App::dumpExit('Не нашли', $row, ['id' => $cid, 'status' => $status]);
  112.                 }
  113.             }
  114.         }
  115.         return $ids;
  116.     }
  117.     /**
  118.      * Обновление ID фильтров свойств коллекции
  119.      * @param $cid
  120.      * @return bool
  121.      * @throws Exception
  122.      */
  123.     public function updateSettingsCollection($cid)
  124.     {
  125.         $this->getParseIdFiltersByAtrr($cid);
  126.         return true;
  127.     }
  128.     /**
  129.      * Фильтрует артикулы по приоритету типа изделия
  130.      * Оставляет только артикулы с типом, имеющим максимальный приоритет (sort DESC)
  131.      *
  132.      * Логика: если есть более приоритетные типы, то менее приоритетные НЕ учитываются.
  133.      * Например, если есть фоновая плитка (приоритет 100) и декор (приоритет 30),
  134.      * то будут использованы только артикулы фоновой плитки, а декор будет проигнорирован.
  135.      *
  136.      * @param array $articles
  137.      * @return array
  138.      */
  139.     private function filterArticlesByTypePriority($articles)
  140.     {
  141.         if (empty($articles)) {
  142.             return $articles;
  143.         }
  144.         // Группируем артикулы по типу
  145.         $articlesByType = [];
  146.         $typePriorities = [];
  147.         foreach ($articles as $article) {
  148.             // Пропускаем артикулы без типа
  149.             if (empty($article['type']) || empty($article['type']['id'])) {
  150.                 continue;
  151.             }
  152.             $typeId $article['type']['id'];
  153.             $typeSort = isset($article['type']['sort']) ? (int)$article['type']['sort'] : 0;
  154.             if (!isset($articlesByType[$typeId])) {
  155.                 $articlesByType[$typeId] = [];
  156.                 $typePriorities[$typeId] = $typeSort;
  157.             }
  158.             $articlesByType[$typeId][] = $article;
  159.         }
  160.         // Если нет артикулов с типами, возвращаем исходный массив
  161.         if (empty($articlesByType)) {
  162.             return $articles;
  163.         }
  164.         // Находим максимальный приоритет (самый высокий sort)
  165.         $maxPriority max($typePriorities);
  166.         // Собираем ТОЛЬКО артикулы типов с максимальным приоритетом
  167.         // Все типы с меньшим приоритетом игнорируются
  168.         $filteredArticles = [];
  169.         foreach ($typePriorities as $typeId => $priority) {
  170.             if ($priority == $maxPriority) {
  171.                 $filteredArticles array_merge($filteredArticles$articlesByType[$typeId]);
  172.             }
  173.             // Типы с priority < $maxPriority не добавляются - они игнорируются
  174.         }
  175.         return $filteredArticles;
  176.     }
  177.     /**
  178.      * Парсим ID фильтров по признакам коллекции и сохраняем их сразу
  179.      * @param $cid
  180.      * @return array
  181.      * @throws ORMException
  182.      * @throws OptimisticLockException
  183.      * @throws Exception
  184.      */
  185.     public function getParseIdFiltersByAtrr($cid)
  186.     {
  187.         $coll $this->repoColl->getByIdForParseAttr($cid);
  188.         if (!$coll) {
  189.             throw new Exception("Ошибка, коллекции ID $cid нет.");
  190.         }
  191.         $interiors $this->repoInterior->getInteriorsByCollection($cid);
  192.         $articles $this->repoArticle->getArticleByCollection($cid);
  193.         $articles $this->sliderService->prepareDiscountAmountForEachArticle($articles);
  194.         // Фильтруем артикулы по приоритету типа
  195.         // Свойства спецэлементов не должны отображаться в характеристиках коллекции под ее заголовком.
  196.         // Там должны быть только свойства фоновой плитки (если вся коллекция из панно, деоров либо мозаики, то их свойства).
  197.         $articles $this->filterArticlesByTypePriority($articles);
  198.         $arr = [];
  199.         // смотрим интерьеры если accessible не TRUE
  200.         if (!$coll->getAccessible()) {
  201.             foreach ($interiors as $interior) {
  202.                 if ($interior['status'] == 1) {
  203.                     $arr_ $this->parseAttr($interior);
  204.                     $arr array_merge($arr$arr_);
  205.                 }
  206.             }
  207.         }
  208.         foreach ($articles as $article) {
  209.             if (empty($article['file'])) {
  210.                 continue;
  211.             }
  212.             $article['surface'] = $this->repoArticle->getSurfaceForArticleFromColl($article['id']);
  213.             $arr_ $this->parseAttr($article);
  214.             $arr array_merge($arr$arr_);
  215.             if (!empty($arr_['left_menu_glased_porcelain_no']) or !empty($arr_['left_menu_glased_porcelain_yes']) or !empty($arr_['left_menu_porcelain'])) {
  216.                 if (!empty($article['thinGranite']) and $article['thinGranite']) {
  217.                     // добавляем признак тонкого керамогранита
  218.                     $arr['left_menu_thin_porcelain_tile'] = [
  219.                         'id'    => 1,
  220.                         'alias' => 'left_menu_thin_porcelain_tile'
  221.                     ];
  222.                 } else {
  223.                     if ($article['thick'] >= 17) {
  224.                         // добавляем признак утолщенного керамогранита
  225.                         $arr['left_menu_thick_porcelain_tile'] = [
  226.                             'id'    => 2,
  227.                             'alias' => 'left_menu_thick_porcelain_tile'
  228.                         ];
  229.                     }
  230.                 }
  231.             }
  232.         }
  233.         $desIds $coll->getAuthorId();
  234.         if ($desIds) {
  235.             foreach ($desIds as $dId) {
  236.                 $arr["designer_{$dId}"] = [
  237.                     'id'  => $dId,
  238.                     'alias' => 'designer',
  239.                 ];
  240.             }
  241.             // добавляем сам стиль дизайнерский
  242.             $arr['left_menu_designer'] = [
  243.                 'id'    => 17,
  244.                 'alias' => 'left_menu_designer'
  245.             ];
  246.         }
  247.         $ids $this->getFilterIds($arr$cid$coll->getStatus());
  248.         $ids array_column($ids'id');
  249.         $coll->setFids($ids);
  250.         App::em()->persist($coll);
  251.         App::em()->flush();
  252.         return $ids;
  253.     }
  254.     /**
  255.      * Проставляем rank и size для фильтров
  256.      * @param $items
  257.      * @return array
  258.      * @throws Exception
  259.      */
  260.     private function setSize($items)
  261.     {
  262.         $rankService App::getContainer()->get('app.service.rank');
  263.         $rankArr = [];
  264.         $items_ = [];
  265.         foreach ($items as $k => $item) {
  266.             $rank = isset($item['rank']) ? intval($item['rank']) : 0;
  267.             $item['rank'] = $rank;
  268.             // ставим дефольный размер для всех
  269.             $item['size'] = $rankService->fontSizeByRank(50);
  270.             $items_[$item['altName']] = $item;
  271.             if (!in_array($item['group.altName'], ['resistance_abrasion''antislip_coff''shade_variation''designer'])) {
  272.                 $rankArr[$item['altName']] = $rank;
  273.             }
  274.         }
  275.         arsort($rankArr);
  276.         $rankArr array_keys($rankArr);
  277.         // количество выделяемых в четверти
  278.         $cnt = (int)ceil(count($rankArr) / 4);
  279.         foreach ($rankArr as $i => $k) {
  280.             if ($i <= $cnt) {
  281.                 // первая четверть
  282.                 $items_[$k]['size'] = $rankService->fontSizeByRank(100);
  283.             } elseif ($i $cnt and $i <= ( $cnt )) {
  284.                 // вторая четверть
  285.                 $items_[$k]['size'] = $rankService->fontSizeByRank(80);
  286.             }
  287.         }
  288.         return $items_;
  289.     }
  290.     /**
  291.      * @param $fids
  292.      * @return string
  293.      * @throws Exception
  294.      */
  295.     public function getFiltersTxt($fids)
  296.     {
  297.         $groups $this->getFilters($fids);
  298.         $str = [];
  299.         foreach ($groups as $r) {
  300.             // убираем surface, вместо нее коэффициенты выводятся
  301.             if ($r['altName'] == 'surface') {
  302.                 continue;
  303.             }
  304.             $s null;
  305.             if ($r['altName'] == 'shade_variation' || $r['altName'] == 'resistance_abrasion') {
  306.                 $s "#{$r['name']}";
  307.             } elseif ($r['altName'] == 'antislip_coff') {
  308.                 $s "#{$r['nameFull']}";
  309.             }
  310.             $l = [];
  311.             foreach ($r['list'] as $item) {
  312.                 $name $item['nameMany'];
  313.                 if ($r['altName'] == 'resistance_abrasion') {
  314.                     $name trim(str_replace('PEI'''$name));
  315.                 }
  316.                 if ($item['altName'] == 'vinil') {
  317.                     $name "<b>{$name}</b>";
  318.                 }
  319.                 $l[] = $name;
  320.             }
  321.             if ($s) {
  322.                 $s "{$s} " implode(', '$l);
  323.             } else {
  324.                 $s "#" implode(' #'$l);
  325.             }
  326.             $str[] = $s;
  327.         }
  328.         $str implode(' '$str);
  329.         return $str;
  330.     }
  331.     /**
  332.      * @param $fids
  333.      * @return string
  334.      * @throws Exception
  335.      */
  336.     public function getFiltersHtml($fids): string
  337.     {
  338.         $groups $this->getFilters($fids);
  339.         return $this->attrToHtml($groups);
  340.     }
  341.     public function attrToHtml($groups)
  342.     {
  343.         $html $this->render(
  344.             '@Web/Collection/attrs.html.twig',
  345.             [
  346.                 'lc'     => App::getCurLocale(),
  347.                 'groups' => $groups,
  348.             ]
  349.         );
  350.         $html trim(trim($html), ',');
  351.         return str_replace(' ,'','$html);
  352.     }
  353.     /**
  354.      * @param $fids
  355.      * @return array
  356.      * @throws Exception
  357.      */
  358.     public function getFilters($fidsbool $forReact false)
  359.     {
  360.         $oRouter App::getRouter();
  361.         $items $this->repoFilter->getForCollSettings($fids);
  362.         $items $this->setSize($items);
  363.         $groups = [];
  364.         // упаковываем по группам
  365.         foreach ($items as $fAlt => $item) {
  366.             $grAlt $item['group.altName'];
  367.             // убираем с вывода свойств все виды изделий
  368.             // https://te.remote.team/#/discus/AE79130E-DE85-4F4A-0434-AFE6A828C451/
  369.             // UPD: Добавил условие для вывода типа изделия в лэере для реакта
  370.             // https://te.remote.team/#/discus/ACF4E5F1-9B69-A9B6-76A5-4EA1F860AC17/
  371.             if ($grAlt == 'type' && !$forReact) {
  372.                 continue;
  373.             }
  374.             // скрыл дизайнеров без коллекций
  375.             // https://te.remote.team/#/discus/BB0C92AC-A6BE-10D6-FC74-D4999FCFE2B2/
  376.             if ($grAlt == 'designer' and !$item['count']) {
  377.                 continue;
  378.             }
  379.             // формируем ссылку на фильтр
  380.             $link $item['slug'];
  381.             if ($link and $item['count']) {
  382.                 $link $oRouter->generate('app_catalog', ['key' => $link]);
  383.             } else {
  384.                 $link null;
  385.             }
  386.             if (empty($groups[$grAlt])) {
  387.                 $nameFull null;
  388.                 switch ($grAlt) {
  389.                     case 'antislip_coff':
  390.                         //$nameFull = App::getTranslator()->trans('catalog.options.non_slip_rating', ['%d%' => '']);
  391.                         $nameFull App::getTranslator()->trans('antislip_full_name', ['%d%' => '']);
  392.                         break;
  393.                     case 'resistance_abrasion':
  394.                         $nameFull App::getTranslator()->trans('catalog.options.pei');
  395.                         break;
  396.                     case 'shade_variation':
  397.                         $nameFull App::getTranslator()->trans('catalog.options.shade_variation', ['%d%' => '']);
  398.                         break;
  399.                 }
  400.                 $name $item['group.name'];
  401.                 // для PEI оставляем верний регистр
  402.                 if ($grAlt != 'resistance_abrasion') {
  403.                     $name StrHelper::toLower($name);
  404.                 }
  405.                 $groups[$grAlt] = [
  406.                     'id'       => $item['group.id'],
  407.                     'altName'  => $item['group.altName'],
  408.                     'name'     => $name,
  409.                     'nameFull' => $nameFull,
  410.                     'list'     => [],
  411.                 ];
  412.             }
  413.             $groups[$grAlt]['list'][$fAlt] = [
  414.                 'id'         => $item['id'],
  415.                 'sphinxId'   => $item['sphinxId'],
  416.                 'sphinxName' => $item['sphinxName'],
  417.                 'altName'    => $item['altName'],
  418.                 'name'       => $item['name'],
  419.                 'nameMany'   => $item['nameMany'],
  420.                 'nameFull'   => $item['nameFull'],
  421.                 'slug'       => $item['slug'],
  422.                 'link'       => $link,
  423.                 'count'      => $item['count'],
  424.                 'rank'       => $item['rank'],
  425.                 'size'       => $item['size'],
  426.             ];
  427.         }
  428.         // переносим в конец
  429.         if (!empty($groups['antislip_coff'])) {
  430.             $shade $groups['antislip_coff'];
  431.             unset($groups['antislip_coff']);
  432.             $groups['antislip_coff'] = $shade;
  433.         }
  434.         // удаляем признак дизайнеров, если есть сами дизайнеры у нас
  435.         if (!empty($groups['designer'])) {
  436.             $designer $groups['style']['list']['designer'];
  437.             $designer['list'] = $groups['designer']['list'];
  438.             $designer['nameFull'] = StrHelper::toLower($designer['nameFull']);
  439.             $groups['designer'] = $designer;
  440.             unset($groups['style']['list']['designer']);
  441.         }
  442.         // переносим в конец
  443.         if (!empty($groups['resistance_abrasion'])) {
  444.             $resistance $groups['resistance_abrasion'];
  445.             unset($groups['resistance_abrasion']);
  446.             $groups['resistance_abrasion'] = $resistance;
  447.         }
  448.         // переносим в конец
  449.         if (!empty($groups['shade_variation'])) {
  450.             $shade $groups['shade_variation'];
  451.             unset($groups['shade_variation']);
  452.             $groups['shade_variation'] = $shade;
  453.         }
  454.         // переносим в конец
  455.         if (!empty($groups['designer'])) {
  456.             $designer $groups['designer'];
  457.             unset($groups['designer']);
  458.             $groups['designer'] = $designer;
  459.         }
  460.         return $groups;
  461.     }
  462.     public function arrayInsert(&$array$position$insert)
  463.     {
  464.         if (is_int($position)) {
  465.             array_splice($array$position0$insert);
  466.         } else {
  467.             $pos   array_search($positionarray_keys($array));
  468.             $array array_merge(
  469.                 array_slice($array0$pos 1),
  470.                 $insert,
  471.                 array_slice($array$pos 1)
  472.             );
  473.         }
  474.     }
  475. }