src/WebBundle/Controller/TileController.php line 274

Open in your IDE?
  1. <?php
  2. namespace WebBundle\Controller;
  3. use AdmBundle\Helper\Adm;
  4. use AdmBundle\Helper\StrAdm;
  5. use DateTime;
  6. use Doctrine\ORM\NonUniqueResultException;
  7. use Doctrine\ORM\NoResultException;
  8. use Exception;
  9. use FlexApp\Classes\CommentableEntityTypes;
  10. use FlexApp\DTO\RequestCatalogDTO;
  11. use FlexApp\Helper\MetaHelper;
  12. use Import1CBundle\Helper\v3\ArticleHelper;
  13. use Import1CBundle\Helper\v3\BiConst;
  14. use Import1CBundle\Helper\v3\InteriorHelper;
  15. use Symfony\Component\HttpFoundation\JsonResponse;
  16. use Symfony\Component\HttpFoundation\RedirectResponse;
  17. use Symfony\Component\HttpFoundation\Request;
  18. use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
  19. use WebBundle\Entity\Visit;
  20. use WebBundle\Helper\App;
  21. use WebBundle\Helper\FilterHelper;
  22. use WebBundle\Helper\ItemHelper;
  23. use WebBundle\Helper\LocaleHelper;
  24. use WebBundle\Helper\PathHelper;
  25. use WebBundle\Helper\RedirectHelper;
  26. use WebBundle\Helper\SearchLogHelper;
  27. use WebBundle\Helper\StatsHelper;
  28. use WebBundle\Helper\StrHelper;
  29. use WebBundle\Helper\UrlHelper;
  30. use WebBundle\Helper\UserHelper;
  31. use WebBundle\Repository\ArticleRepository;
  32. use WebBundle\Repository\BuyOrderRepository;
  33. use WebBundle\Repository\FilterRepository;
  34. use WebBundle\Repository\IdeaRepository;
  35. use WebBundle\Repository\InteriorRepository;
  36. use WebBundle\Repository\PublicationRepository;
  37. use WebBundle\Repository\VisitRepository;
  38. use WebBundle\Service\CollectionService;
  39. use WebBundle\Service\CollectionSettingsService;
  40. use WebBundle\Service\GoogleTranslateService;
  41. use WebBundle\Service\ReviewsService;
  42. use WebBundle\Service\SearchService;
  43. use WebBundle\Service\SliderService;
  44. class TileController extends ExtendedController
  45. {
  46.     /** @required */
  47.     public ArticleRepository $articleRepository;
  48.     /** @required */
  49.     public BuyOrderRepository $buyOrderRepository;
  50.     /** @required */
  51.     public FilterRepository $filterRepository;
  52.     /** @required */
  53.     public IdeaRepository $ideaRepository;
  54.     /** @required */
  55.     public InteriorRepository $interiorRepository;
  56.     /** @required */
  57.     public PublicationRepository $publicationRepository;
  58.     /** @required */
  59.     public VisitRepository $visitRepository;
  60.     /** @required */
  61.     public CollectionService $collectionService;
  62.     /** @required */
  63.     public CollectionSettingsService $collectionSettingsService;
  64.     /** @required */
  65.     public ReviewsService $reviewsService;
  66.     /** @required */
  67.     public SearchService $searchService;
  68.     /** @required */
  69.     public SliderService $sliderService;
  70.     /** @required */
  71.     public GoogleTranslateService $googleTranslateService;
  72.     private int $itemsLimit 20;
  73.     private int $interiorsLimit 20;
  74.     /**
  75.      * @param Request $request
  76.      * @param string $factoryUrl
  77.      * @param string $collectionUrl
  78.      * @param string|null $type
  79.      * @param string|null $elementId
  80.      * @return mixed
  81.      * @throws NoResultException
  82.      * @throws NonUniqueResultException
  83.      * @throws \Doctrine\DBAL\Driver\Exception
  84.      * @throws \Doctrine\DBAL\Exception
  85.      * @throws Exception
  86.      */
  87.     public function indexAction(
  88.         Request $request,
  89.         string $factoryUrl,
  90.         string $collectionUrl,
  91.         ?string $type null,
  92.         ?string $elementId null
  93.     ) {
  94.         // проверка переноса фабрики
  95.         if ($iNeedRedirect RedirectHelper::checkRedirect($factoryUrl$collectionUrl$type$elementId)) {
  96.             return $this->redirect(
  97.                 $this->generateUrl(
  98.                     'app_collection',
  99.                     $iNeedRedirect->getArrayForRedirect()
  100.                 )
  101.             );
  102.         }
  103.         $collection $this->collectionService->findCollectionInDb($factoryUrl$collectionUrl);
  104.         if ($collection['return'] ?? null) {
  105.             return $collection['return'];
  106.         }
  107.         $collection $collection['collection'];
  108.         // нет коллекции
  109.         if (!$collection) {
  110.             throw $this->createNotFoundException('Collection not found');
  111.         }
  112.         // для совместимости
  113.         $key StrAdm::toLower($elementId);
  114.         if ($key == $collection['factory']['url']) {
  115.             return new RedirectResponse(
  116.                 $this->generateUrl('app_collection', [
  117.                     'factoryUrl' => $collection['factory']['url'],
  118.                     'collectionUrl' => $collection['url'],
  119.                 ]),
  120.                 301
  121.             );
  122.         }
  123.         $factoryUrl StrAdm::toLower($factoryUrl);
  124.         $collectionUrl StrAdm::toLower($collectionUrl);
  125.         $collection['interiors'] = $this->interiorRepository->getInteriorsForCollection($collection['id']);
  126.         //todo временно
  127.         if (!empty($collection['process'])) {
  128.             foreach ($collection['interiors'] as $k => $r) {
  129.                 $collection['interiors'][$k]['file'] = preg_replace('#\.jpg#i''.webp'$r['file']);
  130.                 $collection['interiors'][$k]['a_version'] = true;
  131.             }
  132.         }
  133.         $collection['a_version'] = !empty($collection['process']);
  134.         $mainImage PathHelper::pathGenerate('main'$collection);
  135.         // нормализуем данные страны, потом надо будет все это в нормализацию перенести
  136.         $country $collection['factory']['country'];
  137.         $keyCountry $this->filterRepository->getKeyByLeftMenu($country['alias']);
  138.         $collection['factory']['country'] = [
  139.             'id' => $country['id'],
  140.             'code' => StrHelper::toLower($country['code']),
  141.             'alias' => App::trans($country['alias']),
  142.             'url' => $collection['collection']['factory']['country']['url'] = App::generateUrl(
  143.                 'app_catalog',
  144.                 ['key' => StrAdm::toLower($keyCountry)],
  145.                 0
  146.             ),
  147.         ];
  148.         // логика по релизам коллекции
  149.         $releaseYear = !empty($collection['releaseYear']) ? $collection['releaseYear'] : null;
  150.         if ($releaseYear) {
  151.             $curYear date("Y");
  152.             $keyYear $curYear == $releaseYear 'release_year_cur' 'release_year_' $releaseYear;
  153.             if ($releaseYear >= '2017') {
  154.                 $releaseYear = [
  155.                     'year' => $collection['releaseYear'],
  156.                     'text' => App::getTranslator()->trans('collection.releaseYear', ['%d%' => $releaseYear]),
  157.                     'link' => FilterHelper::getUrl([$keyYear]),
  158.                 ];
  159.             } else {
  160.                 $releaseYear null;
  161.             }
  162.         }
  163.         if (!empty($collection['awards'])) {
  164.             $aAwards $this->filterRepository->getAwards($collection['awards']);
  165.             if ($aAwards) {
  166.                 $collection['awards'] = array_values($aAwards);
  167.             }
  168.         }
  169.         // статус использования фильтрации
  170.         $interiorsIds false;
  171.         $itemsIds false;
  172.         $filters $type && $type == 'f' && $key;
  173.         if ($filters) {
  174.             //специальная проверка для псевдофильтра по артикулу
  175.             if (preg_match('#item-(.+)$#'$key$matches)) {
  176.                 $item $this->articleRepository->getItemUrlById($matches[1]);
  177.                 if (!empty($item['url'])) {
  178.                     return new RedirectResponse(
  179.                         $this->generateUrl('app_collection_slideshow', [
  180.                             'factoryUrl' => $collection['factory']['url'],
  181.                             'collectionUrl' => $collection['url'],
  182.                             'type' => 'a',
  183.                             'elementId' => $item['url'],
  184.                         ])
  185.                     );
  186.                 }
  187.             } else {
  188.                 // Разделитель ключей фильтров (новый/старый) — чтобы поддержать старые ссылки и канонизировать в новый формат
  189.                 $sepNew '--';
  190.                 $sepOld '&';
  191.                 try {
  192.                     $sepNewParam App::getParameter('filters.separator.new');
  193.                     if (is_string($sepNewParam) && $sepNewParam !== '') {
  194.                         $sepNew $sepNewParam;
  195.                     }
  196.                 } catch (\Throwable $e) {
  197.                     // noop
  198.                 }
  199.                 try {
  200.                     $sepOldParam App::getParameter('filters.separator.old');
  201.                     if (is_string($sepOldParam) && $sepOldParam !== '') {
  202.                         $sepOld $sepOldParam;
  203.                     }
  204.                 } catch (\Throwable $e) {
  205.                     // noop
  206.                 }
  207.                 $keyNorm $key;
  208.                 if ($sepOld !== $sepNew) {
  209.                     $keyNorm str_replace($sepOld$sepNew$keyNorm);
  210.                 }
  211.                 $keyArr array_values(array_filter(explode($sepNew$keyNorm), static fn ($v) => $v !== ''));
  212.                 if (count($keyArr) > 0) {
  213.                     $keyNew implode($sepNew$keyArr);
  214.                     if ($keyNew != $key) {
  215.                         $url $this->generateUrl(
  216.                             'app_collection',
  217.                             [
  218.                                 '_locale' => App::getCurLocale(true),
  219.                                 'factoryUrl' => $factoryUrl,
  220.                                 'collectionUrl' => $collectionUrl,
  221.                                 'type' => $type,
  222.                                 'elementId' => $keyNew,
  223.                             ]
  224.                         );
  225.                         return new RedirectResponse($url301);
  226.                     }
  227.                 } else {
  228.                     $url $this->generateUrl(/*'app_tile'*/
  229.                         'app_collection',
  230.                         [
  231.                             '_locale' => App::getCurLocale(true),
  232.                             'factoryUrl' => $factoryUrl,
  233.                             'collectionUrl' => $collectionUrl,
  234.                         ]
  235.                     );
  236.                     return new RedirectResponse($url301);
  237.                 }
  238.                 $searchFilter = [
  239.                     'c_id' => [$collection['id']],
  240.                     'inside' => true,
  241.                 ];
  242.                 $reqCatDTO = new RequestCatalogDTO($request$keynull);
  243.                 $oSearchService $this->searchService->setFilter($reqCatDTO$searchFilter);
  244.                 $titleHtml strip_tags($oSearchService->getTitleHtml());
  245.                 $titleHtml preg_replace('/ edit/ui'''$titleHtml);
  246.                 //  перенаправляем на верную страницу каталога
  247.                 if ($oSearchService->isRedirect()) {
  248.                     return $oSearchService->getRedirect();
  249.                 }
  250.                 $data $oSearchService->getSearchData();
  251.                 $keyString serialize($data);
  252.                 $memcache_key 'sphinx' md5($keyString);
  253.                 $searchTrue_cache App::getMemcache()->get($memcache_key);
  254.                 if ($searchTrue_cache !== false) {
  255.                     $searchTrue $searchTrue_cache;
  256.                 } else {
  257.                     $tmp_res $oSearchService->getResult();//App::searchSphinx($data, 0, true);
  258.                     $searchTrue =  $tmp_res['list'];
  259.                    // App::debugExit($searchTrue,App::searchSphinx($data, 0, true));
  260.                     App::getMemcache()->set($memcache_key$searchTrue);
  261.                 }
  262.                 // собираем id подходящих артикулов и интерьеров
  263.                 if (!empty($searchTrue) && $searchTrue != false) {
  264.                     $interiorsIds = !empty($searchTrue[0]['interiors'])
  265.                         ? array_values(array_unique(array_column($searchTrue[0]['interiors'], 'i_id')))
  266.                         : false;
  267.                     $itemsIds = !empty($searchTrue[0]['articles'])
  268.                         ? array_values(array_unique(array_column($searchTrue[0]['articles'], 'a_id')))
  269.                         : false;
  270.                 }
  271.                 $filters = [
  272.                     'list' => $this->collectionService->getSettingsFiltersCollection($oSearchService),
  273.                     'linkToCataloge' => $this->generateUrl('app_catalog', ['key' => $key]),
  274.                 ];
  275.                 $isInteriorsSuitable false;
  276.                 $sortInteriorsIds = [];
  277.                 foreach ($collection['interiors'] as $i => $interior) {
  278.                     if ($interiorsIds && in_array($interior['id'], $interiorsIds)) {
  279.                         $collection['interiorsSuitable'][array_search($interior['id'], $interiorsIds)] = $interior;
  280.                         $sortInteriorsIds[array_search($interior['id'], $interiorsIds)] = $interior['id'];
  281.                         unset($collection['interiors'][$i]);
  282.                         $isInteriorsSuitable true;
  283.                     }
  284.                 }
  285.                 if ($isInteriorsSuitable) {
  286.                     ksort($collection['interiorsSuitable']);
  287.                     $collection['interiorsSuitable'] = array_values($collection['interiorsSuitable']);
  288.                     $collection['interiors'] = array_values($collection['interiors']);
  289.                     $interiorsIds $sortInteriorsIds;
  290.                 }
  291.             }
  292.         } else {
  293.             $this->oSession()->set('searchData', []);
  294.             $collection['interiorsSuitable'] = [];
  295.         }
  296.         $imagePreload null;
  297.         $imagePreload2 null;
  298.         if (!empty($collection['interiorsSuitable'])) {
  299.             foreach ($collection['interiorsSuitable'] as $i => $interior) {
  300.                 $itemInterior InteriorHelper::getInteriorDetails($interior$collection$i$key);
  301.                 $collection['interiorsSuitable'][$i] = $itemInterior;
  302.                 if (!$imagePreload && !empty($itemInterior['pathImg'])) {
  303.                     $imagePreload $itemInterior['pathImg'];
  304.                 }
  305.                 if ($imagePreload && !$imagePreload2 && !empty($itemInterior['pathImg'])) {
  306.                     $imagePreload2 $itemInterior['pathImg'];
  307.                 }
  308.             }
  309.         }
  310.         foreach ($collection['interiors'] as $i => $interior) {
  311.             $itemInterior InteriorHelper::getInteriorDetails($interior$collection$i$key);
  312.             $collection['interiors'][$i] = $itemInterior;
  313.             if (!$imagePreload && !empty($itemInterior['pathImg'])) {
  314.                 $imagePreload $itemInterior['pathImg'];
  315.             }
  316.             if ($imagePreload && !$imagePreload2 && !empty($itemInterior['pathImg'])) {
  317.                 $imagePreload2 $itemInterior['pathImg'];
  318.             }
  319.         }
  320.         $reviews $this->reviewsService->getReviewsByCollectionId((int) $collection['id']);
  321.         $preData $reviews;
  322.         unset($preData['percent']);
  323.         unset($preData['list']);
  324.         unset($preData['stars']);
  325.         // получаем все количество интерьеров и артикулов коллекции
  326.         $interiorsCount $this->interiorRepository->countInteriorsColl((int) $collection['id']);
  327.         $interiorsSuitableCount $interiorsIds $this->interiorRepository->countInteriorsColl(
  328.             (int) $collection['id'],
  329.             $interiorsIds
  330.         ) : 0;
  331.         $favorites $this->ideaRepository->getInteriorsIdeasByToken(UserHelper::getInstance()->getToken());
  332.         $isFreezed in_array($collection['status'], [BiConst::STATE_DISCONTINUEDBiConst::STATE_PROCESSING])
  333.             || in_array($collection['factory']['status'], [BiConst::STATE_DISCONTINUEDBiConst::STATE_PROCESSING]);
  334.         $itemsIdArray $this->articleRepository->getArticlesIdColl($collection['id']);
  335.         $itemsCount $this->articleRepository->countArticlesColl($collection['id'], false$isFreezed);
  336.         $userCurrency LocaleHelper::getCur();
  337.         $currency LocaleHelper::getCurrency();
  338.         // получение минимальной цены арта
  339.         $minPrice $this->articleRepository->getArticleMinPriceByCollectionId($collection['id']);
  340.         $settings '';
  341.         $settingsReact = [];
  342.         if (!empty($collection['fids'])) {
  343.             $settings $this->collectionSettingsService->getFiltersHtml($collection['fids']);
  344.             $settingsReact $this->collectionSettingsService->getFilters($collection['fids'], true);
  345.         }
  346.         $publishDate $collection['publishDate'];
  347.         if ($publishDate instanceof DateTime) {
  348.             $publishDate $publishDate->format('d.m.Y');
  349.         }
  350.         $msg $this->collectionService->getAlertMessageIfCollectionInDiscontinuedState($collection);
  351.         $collection array_merge($collection, [
  352.             'ratingHelp' => App::trans(
  353.                 $collection['rating'] == 1
  354.                     'collection_ideas_summ'
  355.                     'collection_ideas_summ_many',
  356.                 null,
  357.                 ['%count%' => $collection['rating']]
  358.             ),
  359.             'targetItem' => false,
  360.             'settings' => $settings,
  361.             'settingsReact' => $settingsReact,
  362.             'reviews' => $reviews,
  363.             'preData' => $preData,
  364.             'starsOf' => App::trans('product_review_by'null, ['%d%' => $reviews['percent']['d'], '%d1%' => $reviews['count']]),
  365.             'releaseYear' => $releaseYear,
  366.             'publishDate' => $publishDate,
  367.             'linkEdit' => Adm::linkEdit('adm.collection.edit'$collection['id']),
  368.             'linkEditBi' => Adm::linkEdit('bi_collection_show_check'$collection['id']),
  369.             'isFreezed' => $isFreezed,
  370.             'closed' => ($collection['status'] == BiConst::STATE_DISCONTINUED
  371.                 App::trans('catalog_out_of_production')
  372.                 : ($collection['status'] == BiConst::STATE_PROCESSING
  373.                     App::trans('collection_unavailable')
  374.                     : ''
  375.                 )),
  376.             'closedHelp' => $collection['status'] == BiConst::STATE_PROCESSING
  377.                 App::trans('collection_unavailable_manufacture_desc')
  378.                 : '',
  379.             'filters' => $filters,
  380.             'addIdeaUrl' => App::generateUrl('app_ideas_list', ['id' => '']),
  381.             'interiorsUrl' => App::generateUrl('app_tile_interiors', ['id' => $collection['id']]),
  382.             'itemSettingsUrl' => App::generateUrl('app_article_settings', ['id' => 0]),
  383.             'preDataCollectionReviewsUrl' => App::generateUrl('pre_data_collection_reviews', ['id' => $collection['id']]),
  384.             'collectionReviewsUrl' => App::generateUrl('collection_reviews', ['id' => $collection['id']]),
  385.             'interiorsCount' => $interiorsCount,
  386.             'interiorsIds' => $interiorsIds,
  387.             'interiorsSuitableCount' => $interiorsSuitableCount,
  388.             'interiorsMore' => false,
  389.             'interiorsSuitableMore' => false,
  390.             'favorites' => $favorites,
  391.             'itemsUrl' => App::generateUrl('app_tile_items', ['id' => $collection['id']]),
  392.             'itemsCount' => $itemsCount/* + $itemsNoImgCount*/,
  393.             'itemsIdArray' => $itemsIdArray,
  394.             'itemsSuitableCount' => $itemsIds count($itemsIds) : 0,
  395.             'itemsMore' => true,
  396.             'itemsSuitableMore' => true,
  397.             'itemsNoImgMore' => false,//$repoArticle->countArticlesColl($collection['id'], false) > 0,
  398.             'items' => [],
  399.             'itemsSuitable' => [],
  400.             'itemsNoImg' => [],
  401.             'itemsNoImgCount' => 0,//(int)$itemsNoImgCount,
  402.             'itemsIds' => $itemsIds,
  403.             'alsoViewed' => null,
  404.             'alsoViewedUrl' => App::generateUrl('app_article_info', ['collectionId' => $collection['id']]),
  405.             'commentsCounts' => $this->collectionService->getCommentsCountData($collection['unid'] ?? ''),
  406.             'commentsBlockUrl' => App::generateUrl(
  407.                 'app_comment_block',
  408.                 [
  409.                     'entity' => $collection['id'],
  410.                     'type' => CommentableEntityTypes::COLLECTION,
  411.                     'unid' => $collection['unid'] ?? null,
  412.                 ]
  413.             ),
  414.             'collection_reviews' => '',
  415.             'userCountry' => App::getCurCountry(),
  416.             'userLocale' => App::getCurLocale(),
  417.             'userCurrency' => $userCurrency,
  418.             'currency' => $currency,
  419.             'isWithoutVATPrice' => LocaleHelper::isWithoutVATPrice(10) !== false,
  420.             'vatPercent' => LocaleHelper::includeVATAlways(),
  421.             'measureGb' => LocaleHelper::measureGb(),
  422.             'minPrice' => $minPrice,
  423.             'priceSort' => LocaleHelper::getPrMin($collection),
  424.             'priceSortCur' => LocaleHelper::getCur(),
  425.             'priceSortMea' => LocaleHelper::getUserMeasure(),
  426.             'notRegular' => App::trans(
  427.                 'collection.notRegular',
  428.                 null,
  429.                 [
  430.                     '%suspended_brand%' => $collection['factory']['name'],
  431.                 ]
  432.             ),
  433.             'trans' => $this->trans(),
  434.             'msg' => $msg,
  435.         ]);
  436.         // получаем мета по данной странице
  437.         $metaManager MetaHelper::getManager($collection);
  438.         $title $metaManager->getTitle();
  439.         if (!empty($titleHtml)) {
  440.             $titleHtml trim(preg_replace('/(^.+\.)/'''$titleHtml), "\n");
  441.             //  не отсекаем копейки так как >1 округляет < 1 они нужны
  442.             //   $title = preg_replace('/\. .+/', '', $title) . '. ' . $titleHtml;
  443.             $title $title '. ' $titleHtml;
  444.         }
  445.         // нормализуем заголовок
  446.         $collHeader $this->get('translator')->trans(
  447.             $collection['header'],
  448.             [
  449.                 '%collection%' => preg_replace('#\\(.*\\)#isUu'''$collection['ActualName']),
  450.                 '%factory%' => $collection['factory']['ActualName'],
  451.                 '%factoryUrl%' => App::generateUrl(
  452.                     'app_catalog',
  453.                     [
  454.                         'key' => $factoryUrl,
  455.                     ],
  456.                     UrlGeneratorInterface::ABSOLUTE_URL
  457.                 ),
  458.             ]
  459.         );
  460.         // защита от "дурака" когда в переводе добавляют в начало ссылки /;
  461.         $collection['header'] = str_replace('href="/''href="'$collHeader);
  462.         // логика под вывод ссылки на затирки и смеси
  463.         if ($glueUrl $collection['glueUrl']) {
  464.             $glue['url'] = $this->generateUrl('app_collection', [
  465.                 'factoryUrl' => $factoryUrl,
  466.                 'collectionUrl' => $glueUrl,
  467.             ]);
  468.             $glue['text'] = App::getTranslator()->trans('adhesives_grouts_glass_link');
  469.             $collection['glue'] = $glue;
  470.         }
  471.         // сохраняем запись в историю обязательно после нормализации заголовка
  472.         $this->addHistory($collection);
  473.         StatsHelper::addHitsColl($collection['id']);
  474.         SearchLogHelper::save(['url' => App::getRequest()->getUri(), 'title' => $title]);
  475.         // Специальная ссылка на блог
  476.         $blog $this->publicationRepository->find(PublicationController::BLOG_ABOUT_SAMPLE_ID);
  477.         $collection['fastDeliverySamplesUrl'] = UrlHelper::genUrlBlogSingle($blog);
  478.         return $this->renderReact('@Web/Tile/index.html.twig', [
  479.             'imagePreload' => $imagePreload,
  480.             'imagePreload2' => $imagePreload2,
  481.             'mainImage' => $mainImage,
  482.             'type' => $type,
  483.             'meta' => [
  484.                 'title' => $title,
  485.                 'description' => $metaManager->getDescription(),
  486.                 'keywords' => $metaManager->getKeywords(),
  487.                 'specs' => $settingsReact FilterHelper::filterText($settingsReact) : null,
  488.                 'addressCountry' => strtoupper(App::getCurCountry()) ?? '',
  489.             ],
  490.             'initialState' => ['collection' => $collection],
  491.             'ldJson' => $collection['status'] == BiConst::STATE_PUBLISHED && LocaleHelper::getPrMin($collection) > 0,
  492.         ]);
  493.         /**
  494.          * для реакта добавлены переменные
  495.          *
  496.          * interior.pathImg
  497.          * [pathImg] => https://img.tile.expert/img_lb/bisazza/campana/per_sito/ambienti/z_bisazza_campana_2_1.jpg
  498.          * interior.x
  499.          * [x] => 314
  500.          * interior.y
  501.          * [y] => 400
  502.          * interior.imgAlt
  503.          * [imgAlt] => Bisazza-campana-1, handmade,designer Style, Bisazza-campana-2, handmade,designer,handmade,designer Style
  504.          * interior.slideShowInteriorLink
  505.          * [slideShowInteriorLink] => /en/slideshow-images/3246088/i/174155/link
  506.          */
  507.     }
  508.     /**
  509.      * Возвращает порцию артикулов коллекции
  510.      * @param int $id
  511.      * @param ?int $page
  512.      * @return JsonResponse
  513.      * @throws Exception
  514.      * @throws \Doctrine\DBAL\Driver\Exception
  515.      */
  516.     public function itemsAction(int $id, ?int $page 1): JsonResponse
  517.     {
  518.         $limit $this->itemsLimit;
  519.         $repoArticle $this->articleRepository;
  520.         $ordersItems $this->buyOrderRepository->getInfoBuyOrderArticle(UserHelper::getInstance()->getToken(), false$id);
  521.         //загрузка до страницы
  522.         if (App::getRequest()->get('loadAll')) {
  523.             $limit $limit $page;
  524.             $page 1;
  525.         }
  526.         $params = [
  527.             'collection' => $id,
  528.             'offset' => $limit $page $limit,
  529.             'limit' => $limit,
  530.         ];
  531.         // показывать так же снятые артикулы
  532.         if (App::getRequest()->get('isFreezed'false) == 'true') {
  533.             $params['isFreezed'] = 1;
  534.         }
  535.         $suitable App::getRequest()->get('suitable'false) == 'true';
  536.         $keyName 'items' . ($suitable 'Suitable' '');
  537.         if (App::getRequest()->get('itemsIds'false) != 'false') {
  538.             $params[$suitable 'items' 'notItems'] = explode(','App::getRequest()->get('itemsIds'));
  539.         }
  540.         $items $repoArticle->getArticleNativeOpt($paramstrue);
  541.         $items $this->sliderService->prepareDiscountAmountForEachArticle($items);
  542.         // нужно проставить индексы, чтобы react воспринял как массив
  543.         $res = [];
  544.         $i $params['offset'];
  545.         foreach ($items as $item) {
  546.             if ($texture $item['details']['variantImage']['texture'] ?? null) {
  547.                 $item['variantImage']['texture'] = ItemHelper::addMainPic(
  548.                     $texture,
  549.                     StrHelper::toLower($item['details']['file'])
  550.                 );
  551.             }
  552.             if ($picture $item['details']['variantImage']['picture'] ?? null) {
  553.                 $item['variantImage']['picture'] = ItemHelper::addMainPic(
  554.                     $picture,
  555.                     StrHelper::toLower($item['details']['file'])
  556.                 );
  557.             }
  558.             $item['shape'] = empty($item['shape']) ? ['id' => null'alias' => null] : $item['shape'];
  559.             $item['mea'] = App::trans(LocaleHelper::getMeasure($item));
  560.             $item['amount'] = LocaleHelper::getAmount($item);
  561.             $item['orders'] = !empty($ordersItems[$item['id']]) ? $ordersItems[$item['id']] : false;
  562.             $item['suitable'] = $suitable;
  563.             $options ArticleHelper::getTileArticleAddOptions($item);
  564.             $res[$i] = $options;
  565.             $i++;
  566.         }
  567.         if (count($items) == $limit) {
  568.             $params['limit'] = 1;
  569.             $items $repoArticle->getArticleNativeOpt($paramstrue);
  570.             $items $this->sliderService->prepareDiscountAmountForEachArticle($items);
  571.             $more count($items) > 0;
  572.         } else {
  573.             $more false;
  574.         }
  575.         return new JsonResponse(['items' => $res'more' => $more'keyName' => $keyName'nextPage' => $page 1]);
  576.     }
  577.     /**
  578.      * Возвращает порцию интерьеров коллекции
  579.      * @param int $id
  580.      * @param ?int $page
  581.      * @return JsonResponse
  582.      * @throws Exception
  583.      * @internal param string $sort
  584.      */
  585.     public function interiorsAction(int $id, ?int $page 1): JsonResponse
  586.     {
  587.         $limit $this->interiorsLimit;
  588.         $offset $limit $page $limit;
  589.         $suitable App::getRequest()->get('suitable'false) == 'true';
  590.         $keyName 'interiors' . ($suitable 'Suitable' '');
  591.         if (App::getRequest()->get('interiorsIds') && App::getRequest()->get('interiorsIds') != 'false') {
  592.             $params[$suitable 'ids' 'notIds'] = explode(','App::getRequest()->get('interiorsIds'));
  593.         } else {
  594.             $params = [];
  595.         }
  596.         $interiors $this->interiorRepository->getInteriors($id$params);
  597.         $favorites $this->ideaRepository->getInteriorsIdeasByToken(UserHelper::getInstance()->getToken());
  598.         // нужно проставить индексы чтобы react воспринял как массив
  599.         $res = [];
  600.         $i $offset;
  601.         foreach ($interiors as $interior) {
  602.             $alt[] = $interior['name'];
  603.             foreach ($interior['styles'] as $k => $style) {
  604.                 $interiors[$i]['styles'][$k]['alias'] = $alt[] = App::trans($style['alias']);
  605.             }
  606.             foreach ($interior['textures'] as $k => $texture) {
  607.                 $interiors[$i]['textures'][$k]['alias'] = $alt[] = App::trans($texture['alias']);
  608.             }
  609.             $interiors[$i]['alt'] = join(', '$alt);
  610.             $interior['favorite'] = !empty($favorites[$interior['id']]);
  611.             $interior['suitable'] = $suitable;
  612.             $res[$i] = $interior;
  613.             $i++;
  614.         }
  615.         return new JsonResponse(['interiors' => $res'more' => false'keyName' => $keyName]);
  616.     }
  617.     /**
  618.      * Добавляет запись в историю просмотров
  619.      *
  620.      * @param $collection
  621.      * @throws NoResultException
  622.      * @throws NonUniqueResultException
  623.      * @throws Exception
  624.      */
  625.     private function addHistory($collection)
  626.     {
  627.         $headerVisited implode(' ', [
  628.             $collection['ActualName'],
  629.             $this->get('translator')->trans('collection_by'),
  630.             $collection['factory']['ActualName'],
  631.         ]);
  632.         $icon $collection['path'] . 'main.jpg';
  633.         $factoryUrl StrAdm::toLower($collection['factory']['url']);
  634.         $collectionUrl StrAdm::toLower($collection['url']);
  635.         // специально чтобы ссылки с выбранными фильтрами рассматривались как стандартная ссылка на коллекцию
  636.         $url $this->generateUrl(
  637.             'app_collection',
  638.             [
  639.                 '_locale' => App::getCurLocale(true),
  640.                 'factoryUrl' => $factoryUrl,
  641.                 'collectionUrl' => $collectionUrl,
  642.             ],
  643.             0
  644.         );
  645.         $this->visitRepository->setVisit(
  646.             $headerVisited,
  647.             $icon,
  648.             $url,
  649.             null,
  650.             Visit::TYPE_VISIT['COLLECTION'],
  651.             $collection['id']
  652.         );
  653.     }
  654.     /**
  655.      * Переводы для страницы коллекции
  656.      * @return array
  657.      * @throws \Doctrine\DBAL\Driver\Exception
  658.      * @throws \Doctrine\DBAL\Exception
  659.      * @throws Exception
  660.      */
  661.     private function trans(): array
  662.     {
  663.         $translator App::getTranslator();
  664.         return [
  665.             'loc' => App::getCurLocale(true),
  666.             'lang' => App::getCurLocale(),
  667.             'settings' => $translator->trans('collection_marks'),
  668.             'added' => $translator->trans('collection_added'),
  669.             'star' => $translator->trans('reviews.stars.one'),
  670.             'stars' => $translator->trans('reviews.stars.many'),
  671.             'reviewShowAll' => $translator->trans('product_review_show_all'),
  672.             'catalogMoreDown' => $translator->trans('catalog_more_down'),
  673.             'catalogMoreUp' => $translator->trans('catalog_more_up'),
  674.             'descCollectionHeader' => $translator->trans('descCollectionHeader'),
  675.             'commentAddQuestion' => $translator->trans('comment_add_question'),
  676.             'collectionVideos' => $translator->trans('collection.videos'),
  677.             'collectionBack' => $translator->trans('collection_back'),
  678.             'productReview' => [
  679.                 $translator->trans('product_review'),
  680.                 $translator->trans('product_review_'),
  681.                 $translator->trans('product_reviews'),
  682.             ],
  683.             'comments' => $translator->trans('blog_comments'),
  684.             'of' => $translator->trans('of'),
  685.             'more' => $translator->trans('show_more'),
  686.             'effect' => $translator->trans('left_menu_effect'),
  687.             'style' => $translator->trans('left_menu_style'),
  688.             'reviews' => [
  689.                 'heading' => $translator->trans('collection_customer_reviews'),
  690.                 'validUser' => $translator->trans('product_validUser'),
  691.                 'reply' => $translator->trans('reply_comment'),
  692.                 'edit' => $translator->trans('edit'),
  693.                 'save' => $translator->trans('save'),
  694.                 'cancel' => $translator->trans('cancel'),
  695.                 'pluralForms' => $translator->trans('review.plural_forms'),
  696.             ],
  697.             'item' => [
  698.                 'collection' => $translator->trans('collection_name'),
  699.                 'factory' => $translator->trans('catalog_factory'),
  700.                 'characteristics' => $translator->trans('article_characteristics'),
  701.                 'tile' => $translator->trans('footer_tile'),
  702.                 'item' => $translator->trans('collection_article'),
  703.                 'header' => $translator->trans('collection_articles_d'),
  704.                 'formats' => $translator->trans('article_item_formats'),
  705.                 'cm' => $translator->trans('left_menu_cm'),
  706.                 'inch' => '″',
  707.                 'diameter' => $translator->trans('article_item_diameter'),
  708.                 'pcs' => $translator->trans('measure_unit'),
  709.                 'pcsPlural' => $translator->trans('measure_units'),
  710.                 'set' => $translator->trans('measure_set'),
  711.                 'sets' => $translator->trans('measure_sets'),
  712.                 'price' => $translator->trans('catalog_price'),
  713.                 'vat' => [
  714.                     'label' => $translator->trans('vat.label'),
  715.                     'included' => $translator->trans('vat.included'),
  716.                     'excluded' => $translator->trans('vat.excluded'),
  717.                 ],
  718.                 'info' => $translator->trans('article_item_more_info'),
  719.                 'infoLess' => $translator->trans('article_item_less_info'),
  720.                 'order' => [
  721.                     'help' => $translator->trans('buyOrder.help'),
  722.                     'ceilToPallet' => $translator->trans('collection_ceil_to_pallet'),
  723.                     'atentionPallet' => $translator->trans('collection_atention_pallet'),
  724.                     'popupAutoChangeCountArts' => $translator->trans('popup_auto_change_count_arts'),
  725.                 ],
  726.                 'up' => $translator->trans('article.price.up'),
  727.                 'down' => $translator->trans('article.price.down'),
  728.                 'readMore' => $translator->trans('read_more'),
  729.                 'titleAddedArticle' => $translator->trans('buyOrder.titleAddedArticle'),
  730.                 'addIntoOrder' => $translator->trans('buy_order.add_into_order'),
  731.                 'headerOrderAdd' => $translator->trans('header_order_add'),
  732.                 'draft' => $translator->trans('order.status.draft'),
  733.                 'typeMain' => $translator->trans('article.type.main'),
  734.                 'typeSample' => $translator->trans('article.type.sample'),
  735.                 'sampleText' => $translator->trans('article.type.sampleText'),
  736.                 'typeCompare' => $translator->trans('article.type.compare'),
  737.                 'compareText' => $translator->trans('article.type.compareText'),
  738.                 'type3d' => $translator->trans('article.type.3d'),
  739.                 'text3d' => $translator->trans('article.type.3dText'),
  740.                 'articleOrderOver' => $translator->trans('article.order.over'),
  741.                 'articleOrderMultiplies' => $translator->trans('article.order.multiplies'),
  742.                 'articlePricePerPallet' => $translator->trans('article.price.perPallet'),
  743.                 'articlePriceNoLessPallet' => $translator->trans('article.price.noLessPallet'),
  744.                 'articlePricePalleteFrom' => $translator->trans('article.pricePallete.from'),
  745.                 'articlePriceFrom' => $translator->trans('article.price.from'),
  746.                 'articlePricePalleteUnder' => $translator->trans('article.pricePallete.under'),
  747.                 'articlePriceFromTo' => $translator->trans('article.price.from_to'),
  748.                 'articlePriceFromLine' => $translator->trans('catalog_price_from'),
  749.                 'articlePriceToLine' => $translator->trans('dimensions_to'),
  750.                 'boxArticleSingle' => $translator->trans('box_article_single'),
  751.                 'boxArticlePlural' => $translator->trans('box_article_plural'),
  752.                 'articleNoImgTitle' => $translator->trans('article.no_img'),
  753.                 'collectionAddOrder' => $translator->trans('collection_add_order'),
  754.                 'discountinued' => $translator->trans('article.discountinued'),
  755.                 'tempNoUKShippingMsg' => $translator->trans('msg.uk'),
  756.                 'tempUnavailable' => $translator->trans('article.temp_unavailable'),
  757.             ],
  758.             'sample' => [
  759.                 'folder' => $translator->trans('sample.folder.text'),
  760.                 'original' => $translator->trans('sample.original.text'),
  761.                 'piece' => $translator->trans('sample.piece.text'),
  762.                 'title' => $translator->trans('samplePopup.title'),
  763.                 'noMoreShow' => $translator->trans('samplePopup.noMoreShow'),
  764.                 'withDecl' => $translator->trans('article.type.sampleWithDecl'),
  765.                 'alreadyAdded' => $translator->trans('sample_already_added.header'),
  766.             ],
  767.             'interior' => [
  768.                 'header' => $translator->trans('collection_interiors_d'),
  769.                 'msg' => $translator->trans('collection.interior.msg'),
  770.                 'ideaHelp' => [
  771.                     'added' => $translator->trans('idea_msg_added'),
  772.                     'normal' => $translator->trans('idea_msg_normal'),
  773.                     'normalMany' => $translator->trans('idea_msg_normal_many'),
  774.                     'normalZero' => $translator->trans('idea_msg_normal_zero'),
  775.                     'collectionIdeasShort' => $translator->trans('collection_ideas_short'),
  776.                 ],
  777.             ],
  778.             'sampleBlog' => $this->publicationRepository->getUrlBlogExpressSamples(),
  779.             'unavailableFreezedDescArticle' => $translator->trans('collection_unavailable_freezed_desc_article'),
  780.             'collectionTempUnavailable' => $translator->trans('collection.temp_unavailable'),
  781.             'notAvailableInRuCountry' => $translator->trans('product_not_available_in_ru'),
  782.             'no_results' => $translator->trans('catalog_msg.no_results'),
  783.             'searchResult' => $translator->trans('search_result'),
  784.             'suitableElements' => $translator->trans('collection_suitable_elements'),
  785.             'otherElements' => $translator->trans('collection_other_elements'),
  786.             'anonymously' => $translator->trans('anonymously'),
  787.         ];
  788.     }
  789.     public function translateArticleAction(int $elementIdstring $lang): JsonResponse
  790.     {
  791.         return new JsonResponse([
  792.             'result' => $this->googleTranslateService->translateCommentForArticle($elementId$lang),
  793.         ]);
  794.     }
  795. }