src/Application/Controller/CatalogController.php line 100

Open in your IDE?
  1. <?php
  2. namespace App\Application\Controller;
  3. use App\Admin\Document\AbstractSEODocument;
  4. use App\Admin\Document\Article;
  5. use App\Admin\Document\Category;
  6. use App\Admin\Document\Color;
  7. use App\Admin\Document\Page;
  8. use App\Admin\Document\Product;
  9. use App\Admin\Document\Property;
  10. use App\Admin\Document\PropertyValue;
  11. use App\Admin\Document\Tag;
  12. use Symfony\Component\HttpFoundation\Cookie;
  13. use Symfony\Component\HttpFoundation\JsonResponse;
  14. use Symfony\Component\HttpFoundation\Request;
  15. use Symfony\Component\HttpFoundation\Response;
  16. use \Symfony\Component\Routing\Annotation\Route;
  17. class CatalogController extends AbstractController
  18. {
  19.     const CATALOG_URL 'catalog';
  20.     const RECENTLY_WATCHED_PRODUCTS_COOKIE_NAME 'RecentlyWatchedProducts';
  21.     const RECENTLY_WATCHED_PRODUCTS_LIMIT 8;
  22.     protected function getDefaultItemCountPerPage(){
  23.         return 9;
  24.     }
  25.     protected function setCategorySeo(Category $document$locale$page null) {
  26.         $document = clone $document;
  27.         if (!$document->getSeoTitle()) {
  28.             $document->setSeoTitle('Купить ' $document->getName($locale)
  29.                 . ' от производителя, производство СПб');
  30.         }
  31.         if (!$document->getSeoDescription()) {
  32.             $document->setSeoDescription($document->getName($locale)
  33.                 . ' от производителя Cosca. Производство в Санкт-Петербурге. Оптовые и розничные продажи. Доставка транспортной компанией по всей России и странам СНГ');
  34.         }
  35.         $this->setSeo(
  36.             $document,
  37.             $locale,
  38.             $page
  39.         );
  40.     }
  41.     protected function setProductSeo(Product $document$locale$page null) {
  42.         $document = clone $document;
  43.         if (!$document->getSeoTitle()) {
  44.             $document->setSeoTitle($document->getName($locale)
  45.                 . ', купить в интернет-магазине Decostore'
  46.                 ', цена ' $document->getPrice());
  47.         }
  48.         if (!$document->getSeoDescription()) {
  49.             $document->setSeoDescription($document->getName($locale)
  50.                 . ' — предлагаем к покупке в интернет-магазине Decostore оптом и в розницу по доступной цене. Доставка по всей России и странам СНГ. Собственное производство, широкий ассортимент, всегда в наличии.');
  51.         }
  52.         $this->setSeo(
  53.             $document,
  54.             $locale,
  55.             $page
  56.         );
  57.     }
  58.     /**
  59.      * @Route("/catalog", name="catalog")
  60.      * @param Request $request
  61.      * @return \Symfony\Component\HttpFoundation\Response
  62.      */
  63.     public function index(Request $request)
  64.     {
  65.         $page $this->getCatalogPage();
  66.         $this->setSeo($page$request->getLocale());
  67.         return $this->render('application/catalog/index.html.twig', [
  68.             'page' => $page,
  69.         ]);
  70.     }
  71.     /**
  72.      * @Route("/search", name="search")
  73.      * @param Request $request
  74.      * @return \Symfony\Component\HttpFoundation\Response
  75.      */
  76.     public function search(Request $request)
  77.     {
  78.         return $this->getSearchViewModel($request);
  79.     }
  80.     /**
  81.      * @Route("/catalog/{url}", name="catalog2")
  82.      * @param Request $request
  83.      * @return \Symfony\Component\HttpFoundation\Response
  84.      */
  85.     public function level2(Request $request$url)
  86.     {
  87.         /**
  88.          * @var Category $category
  89.          */
  90.         $category $this->getDocumentRepository(Category::class)->findOneBy(['url' => $url'active' => true]);
  91.         if (!$category) {
  92.             throw $this->createNotFoundException("Category with url '{$url}' not active");
  93.         }
  94.         return $this->getCatalogViewModel($request$category);
  95.     }
  96.     /**
  97.      * @Route("/catalog/{parentUrl}/{url}", name="catalog3")
  98.      * @param Request $request
  99.      * @return \Symfony\Component\HttpFoundation\Response
  100.      */
  101.     public function level3(Request $request$parentUrl$url)
  102.     {
  103.         /**
  104.          * @var Category $parent
  105.          */
  106.         $parent $this->getDocumentRepository(Category::class)->findOneBy(['url' => $parentUrl'active' => true]);
  107.         if (!$parent) {
  108.             throw $this->createNotFoundException("Category with url '{$parentUrl}' does not exist or not active");
  109.         }
  110.         /**
  111.          * @var Category $category
  112.          */
  113.         $category $this->getDocumentRepository(Category::class)->findOneBy(['url' => $url'active' => true'parent' => $parent->getId()]);
  114.         if (!$category) {
  115.             throw $this->createNotFoundException("Category with url '{$url}' does not have parent {$parentUrl} or not active");
  116.         }
  117.         return $this->getCatalogViewModel($request$category);
  118.     }
  119.     private function getCatalogPage() {
  120.         $page $this->getDocumentRepository(Page::class)->findOneBy(['url' => self::CATALOG_URL'active' => true]);
  121.         if (!$page) {
  122.             throw $this->createNotFoundException("Page with url " self::CATALOG_URL " not found or not active");
  123.         }
  124.         return $page;
  125.     }
  126.     /**
  127.      * @param Request $request
  128.      * @param Category|null $category
  129.      * @param string $searchText
  130.      * @return array
  131.      * @throws \Doctrine\ODM\MongoDB\MongoDBException
  132.      */
  133.     private function getItemsParams(Request $request$category$searchText '') {
  134.         $filtered false;
  135.         $priceFrom str_replace(' '''$request->get('priceFrom'));
  136.         $priceTo str_replace(' '''$request->get('priceTo'));
  137.         $color $request->get('color');
  138.         $properties $request->get('property', []);
  139.         $sort $request->get('sort');
  140.         
  141.         // Находим популярные фильтры, если есть категория
  142.         $effectivePopularFilters = [];
  143.         if ($category) {
  144.             $effectivePopularFilters $category->getEffectivePopularFilters();
  145.         }
  146.         $qb $this->getQueryBuilder(Product::class);
  147.         $qb->field('active')->equals(true);
  148.         if ($category) {
  149.             if ($category->getSale()) {
  150.                 $qb->field('sale')->equals(true);
  151.             } else {
  152.                 $qb->field('categories')->equals($category->getId());
  153.                 $qb->field('sale')->notEqual(true);
  154.             }
  155.         } elseif ($searchText) {
  156.             $expr $qb->expr();
  157.             $regex = new \MongoDB\BSON\Regex('.*' preg_quote(trim($searchText)) . '.*''i');
  158.             $propertyValueQb $this->getQueryBuilder(PropertyValue::class);
  159.             $propertyValueQb->field('name')->equals($regex);
  160.             $pipeline = [
  161.                 ['$match' => $propertyValueQb->getQueryArray()],
  162.                 ['$group' => ['_id' => '$_id']],
  163.             ];
  164.             $collection $this->getDocumentManager()->getDocumentCollection(PropertyValue::class);
  165.             $docs $collection->aggregate($pipeline$this->options);
  166.             $propertyValueIds array_column($docs->toArray(), '_id');
  167.             $expr->addOr($qb->expr()->field('properties.propertyValue')->in($propertyValueIds));
  168.             $expr->addOr($qb->expr()->field('name')->equals($regex));
  169.             $expr->addOr($qb->expr()->field('text')->equals($regex));
  170.             $expr->addOr($qb->expr()->field('shortText')->equals($regex));
  171.             $expr->addOr($qb->expr()->field('sku')->equals($regex));
  172.             $qb->addAnd($expr);
  173.         } else {
  174.             $qb->field('name')->equals([]); // no results
  175.         }
  176.         $query $qb->getQueryArray();
  177.         if ($priceFrom) {
  178.             $qb->field('price')->gte((float)$priceFrom);
  179.             $filtered true;
  180.         }
  181.         if ($priceTo) {
  182.             $qb->field('price')->lte((float)$priceTo);
  183.             $filtered true;
  184.         }
  185.         if ($color) {
  186.             if (is_array($color)) {
  187.                 if (count($color) > 0) {
  188.                     $expr $qb->expr();
  189.                     foreach ($color as $colorId) {
  190.                         if ($colorId) {
  191.                             $expr->addOr($qb->expr()->field('color')->equals($colorId));
  192.                             $filtered true;
  193.                         }
  194.                     }
  195.                     $qb->addAnd($expr);
  196.                 }
  197.             } else {
  198.                 $qb->field('color')->equals($color);
  199.                 $filtered true;
  200.             }
  201.         }
  202.         if (!empty($properties)) {
  203.             foreach ($properties as $propertyId => $propertyValueId) {
  204.                 if ($propertyValueId) {
  205.                     if (is_array($propertyValueId)) {
  206.                         $expr $qb->expr();
  207.                         
  208.                         // Проверяем, является ли массив ассоциативным (чекбоксы) или нумерованным (select multiple)
  209.                         $isAssoc false;
  210.                         foreach (array_keys($propertyValueId) as $key) {
  211.                             if (!is_numeric($key)) {
  212.                                 $isAssoc true;
  213.                                 break;
  214.                             }
  215.                         }
  216.                         
  217.                         if ($isAssoc) {
  218.                             // Для чекбоксов (ассоциативный массив)
  219.                             foreach ($propertyValueId as $id => $v) {
  220.                                 if ($v) {
  221.                                     $expr->addOr($qb->expr()->field('propertyMap1C.' $propertyId)->equals($id));
  222.                                     $filtered true;
  223.                                 }
  224.                             }
  225.                         } else {
  226.                             // Для select multiple (нумерованный массив)
  227.                             foreach ($propertyValueId as $value) {
  228.                                 if ($value) {
  229.                                     $expr->addOr($qb->expr()->field('propertyMap1C.' $propertyId)->equals($value));
  230.                                     $filtered true;
  231.                                 }
  232.                             }
  233.                         }
  234.                         
  235.                         $qb->addAnd($expr);
  236.                     } else {
  237.                         $qb->field('propertyMap1C.' $propertyId)->equals($propertyValueId);
  238.                         $filtered true;
  239.                     }
  240.                 }
  241.             }
  242.         }
  243.         $qb->sort('inStock''desc');
  244.         switch ($sort) {
  245.             case 'name':
  246.                 $qb->sort('name');
  247.                 $qb->sort('price');
  248.                 $qb->sort('popularity''desc');
  249.                 break;
  250.             case 'priceDesc':
  251.                 $qb->sort('price''desc');
  252.                 $qb->sort('name''desc');
  253.                 $qb->sort('popularity''desc');
  254.                 break;
  255.             case 'priceAsc':
  256.                 $qb->sort('price');
  257.                 $qb->sort('name');
  258.                 $qb->sort('popularity''desc');
  259.                 break;
  260.             default:
  261.                 $qb->sort('popularity''desc');
  262.                 $qb->sort('price');
  263.                 $qb->sort('name');
  264.         }
  265.         $qb->sort('_id');
  266.         if ($request->get('debug')) {
  267.             var_dump($qb->getQuery()->getQuery());
  268.             exit;
  269.         }
  270.         $pagination $this->paginate($qb);
  271.         $products $pagination->getItems();
  272.         $pipeline = [
  273.             ['$match' => $query],
  274.             ['$group' => [
  275.                 '_id' => '$color',
  276.             ]],
  277.         ];
  278.         $collection $this->getDocumentManager()->getDocumentCollection(Product::class);
  279.         $docs $collection->aggregate($pipeline$this->options);
  280.         $colorIds = [];
  281.         foreach ($docs as $doc) {
  282.             $colorIds[] = $doc['_id'];
  283.         }
  284.         $qb $this->getQueryBuilder(Color::class);
  285.         $qb->field('_id')->in($colorIds);
  286.         $qb->sort('name''ASC');
  287.         $colors = [];
  288.         foreach ($qb->getQuery()->execute() as $colorObj) {
  289.             $colors[$colorObj->getId()] = $colorObj;
  290.         }
  291.         $filters = [];
  292.         $popularFilters = []; // Массив для популярных фильтров
  293.         /**
  294.          * @var Property $property
  295.          */
  296.         foreach ($this->getDocumentRepository(Property::class)->findBy(['active' => true'id1C' => ['$exists' => true]], ['name' =>'asc']) as $property) {
  297.             if (!$property->getId1C()) {
  298.                 continue;
  299.             }
  300.             $pipeline = [
  301.                 ['$match' => array_merge($query, ['propertyMap1C.' $property->getId1C() => ['$exists' => true]])],
  302.                 ['$group' => [
  303.                     '_id' => '$propertyMap1C.' $property->getId1C(),
  304.                 ]],
  305.             ];
  306.             $docs $collection->aggregate($pipeline$this->options);
  307.             $valueIds = [];
  308.             foreach ($docs as $doc) {
  309.                 $valueIds[] = $doc['_id'];
  310.             }
  311.             if (count($valueIds)) {
  312.                 $qb $this->getQueryBuilder(PropertyValue::class);
  313.                 $qb->field('id1C')->in($valueIds);
  314.                 $qb->sort('name''ASC');
  315.                 $values = [];
  316.                 /**
  317.                  * @var PropertyValue $value
  318.                  */
  319.                 foreach ($qb->getQuery()->execute() as $value) {
  320.                     $values[$value->getId1C()] = $value;
  321.                 }
  322.                 foreach ($valueIds as $id) {
  323.                     if (!isset($values[$id])) {
  324.                         $pv = new PropertyValue();
  325.                         $pv->setName($id);
  326.                         $pv->setId1C($id);
  327.                         $values[$id] = $pv;
  328.                     }
  329.                 }
  330.                 uasort($values,
  331.                     /**
  332.                      * @param PropertyValue $a
  333.                      * @param PropertyValue $b
  334.                      */
  335.                     function ($a$b) {
  336.                         return $a->compare($b);
  337.                     }
  338.                 );
  339.                 $properties $request->get('property', []);
  340.                 if ($property->isCheckboxFilter()) {
  341.                     $selected = [];
  342.                     if (isset($properties[$property->getId1C()])) {
  343.                         foreach ($properties[$property->getId1C()] as $id => $v) {
  344.                             if ($v) {
  345.                                 $selected[] = $id;
  346.                             }
  347.                         }
  348.                     }
  349.                 } else {                   
  350.                     $selected = [];
  351.                     if (isset($properties[$property->getId1C()])) {
  352.                         $propValues $properties[$property->getId1C()];
  353.                         if (is_array($propValues)) {
  354.                             foreach ($propValues as $value) {
  355.                                 if ($value) {
  356.                                     $selected[] = $value;
  357.                                 }
  358.                             }
  359.                         } elseif ($propValues) {
  360.                             $selected[] = $propValues;
  361.                         }
  362.                     }
  363.                 }
  364.                 $filters[] = [
  365.                     'property' => $property,
  366.                     'values' => $values,
  367.                     'selected' => $selected
  368.                 ];
  369.                 
  370.                 // Проверяем, является ли фильтр популярным
  371.                 if ($category && !empty($effectivePopularFilters) && in_array($property->getId1C(), $effectivePopularFilters)) {
  372.                     $popularFilters[] = [
  373.                         'property' => $property,
  374.                         'values' => $values,
  375.                         'selected' => $selected
  376.                     ];
  377.                 }
  378.             }
  379.         }
  380.         
  381.         // Находим популярные фильтры, если есть категория
  382.         $popularFilters = [];
  383.         if ($category) {
  384.             $effectivePopularFilters $category->getEffectivePopularFilters();
  385.             
  386.             if (!empty($effectivePopularFilters)) {
  387.                 $propertyFilters = [];
  388.                 $specialFilters = [];
  389.                 
  390.                 // Разделяем фильтры на свойства и специальные
  391.                 foreach ($effectivePopularFilters as $filterId) {
  392.                     if ($filterId === \App\Admin\Document\Category::SPECIAL_FILTER_PRICE) {
  393.                         $specialFilters[] = $filterId;
  394.                     } else if ($filterId === \App\Admin\Document\Category::SPECIAL_FILTER_COLOR) {
  395.                         $specialFilters[] = $filterId;
  396.                     } else {
  397.                         $propertyFilters[] = $filterId;
  398.                     }
  399.                 }
  400.                 
  401.                 // Получаем диапазон цен для фильтра цены
  402.                 $priceRange = [00];
  403.                 if (in_array(\App\Admin\Document\Category::SPECIAL_FILTER_PRICE$specialFilters)) {
  404.                     $priceRange $this->getMinMaxPrices(['query' => $qb->getQueryArray()]);
  405.                 }
  406.                 $minFilterPrice $priceRange[0];
  407.                 $maxFilterPrice $priceRange[1];
  408.                 
  409.                 // Получаем обычные свойства
  410.                 if (!empty($propertyFilters)) {
  411.                     $popularProperties $this->getDocumentRepository(\App\Admin\Document\Property::class)
  412.                         ->findBy(['id1C' => ['$in' => $propertyFilters]]);
  413.                     
  414.                     // Преобразуем в массив для популярных фильтров
  415.                     foreach ($popularProperties as $property) {
  416.                         if ($property->isCheckboxFilter()) {
  417.                             continue; // Пропускаем чекбоксы, так как они не подходят для быстрых фильтров
  418.                         }
  419.                         
  420.                         // Находим соответствующий фильтр в общем списке
  421.                         foreach ($filters as $filter) {
  422.                             if ($filter['property']->getId1C() === $property->getId1C()) {
  423.                                 $popularFilters[] = [
  424.                                     'property' => $property,
  425.                                     'values' => $filter['values'],
  426.                                     'selected' => $filter['selected'],
  427.                                     'type' => 'property'
  428.                                 ];
  429.                                 break;
  430.                             }
  431.                         }
  432.                     }
  433.                 }
  434.                 
  435.                 // Добавляем специальные фильтры
  436.                 foreach ($specialFilters as $specialFilter) {
  437.                     if ($specialFilter === \App\Admin\Document\Category::SPECIAL_FILTER_PRICE) {
  438.                         $popularFilters[] = [
  439.                             'property' => (object)['id1C' => 'price''name' => 'Цена''checkboxFilter' => false],
  440.                             'values' => [], // Для цены не используются значения
  441.                             'selected' => [$priceFrom$priceTo],
  442.                             'type' => 'price',
  443.                             'min' => $minFilterPrice,
  444.                             'max' => $maxFilterPrice
  445.                         ];
  446.                     } else if ($specialFilter === \App\Admin\Document\Category::SPECIAL_FILTER_COLOR && !empty($colors)) {
  447.                         $popularFilters[] = [
  448.                             'property' => (object)['id1C' => 'color''name' => 'Цвет''checkboxFilter' => false],
  449.                             'values' => $colors,
  450.                             'selected' => $color,
  451.                             'type' => 'color'
  452.                         ];
  453.                     }
  454.                 }
  455.             }
  456.         }
  457.         
  458.         return [
  459.             'category' => $category,
  460.             'products' => $products,
  461.             'pagination' => $pagination->getPaginationData(),
  462.             'page' => $pagination->getCurrentPageNumber(),
  463.             'priceFrom' => (int)$priceFrom,
  464.             'priceTo' => (int)$priceTo,
  465.             'color' => $color,
  466.             'filtered' => $filtered,
  467.             'sort' => $sort,
  468.             'query' => $query,
  469.             'view' => $request->get('view'),
  470.             'colors' => $colors,
  471.             'filters' => $filters,
  472.             'popularFilters' => $popularFilters,
  473.         ];
  474.     }
  475.     private $options = ['cursor' => true'allowDiskUse' => true];
  476.     private function getCatalogViewModel(Request $requestCategory $category) {
  477.         $catalogPage $this->getCatalogPage();
  478.         $categoryList $this->getDocumentRepository(Category::class)->findBy(['active' => true'parent' => null],
  479.             ['orderNumber' => 'asc''name' => 'asc']);
  480.         $this->setCategorySeo($category$request->getLocale());
  481.         $itemsParams $this->getItemsParams($request$category);
  482.         $prices $this->getMinMaxPrices($itemsParams);
  483.         $minPrice $prices[0];
  484.         $maxPrice $prices[1];
  485.         unset($itemsParams['query']);
  486.         return $this->render('application/catalog/filteredResults.html.twig'array_merge($itemsParams, [
  487.             'searchText' => null,
  488.             'searchResultsQuantity' => null,
  489.             'catalogPage' => $catalogPage,
  490.             'category' => $category,
  491.             'categoryList' => $categoryList,
  492.             'minPrice' => (int)$minPrice,
  493.             'maxPrice' => (int)$maxPrice,
  494.             'pageNumber' => $request->get('page'),
  495.         ]));
  496.     }
  497.     private function getSearchViewModel(Request $request) {
  498.         $categoryList $this->getDocumentRepository(Category::class)->findBy(['active' => true'parent' => null],
  499.             ['orderNumber' => 'asc''name' => 'asc']);
  500.         $this->setSeoByUrl('search'$request->getLocale());
  501.         $searchText $request->get('searchText');
  502.         $itemsParams $this->getItemsParams($requestnull$searchText);
  503.         $prices $this->getMinMaxPrices($itemsParams);
  504.         $minPrice $prices[0];
  505.         $maxPrice $prices[1];
  506.         unset($itemsParams['query']);
  507.         return $this->render('application/catalog/filteredResults.html.twig'array_merge($itemsParams, [
  508.             'searchText' => $searchText,
  509.             'searchResultsQuantity' => isset($itemsParams['pagination']['totalCount']) ? $itemsParams['pagination']['totalCount'] : null,
  510.             'catalogPage' => null,
  511.             'category' => null,
  512.             'categoryList' => $categoryList,
  513.             'minPrice' => (int)$minPrice,
  514.             'maxPrice' => (int)$maxPrice,
  515.             'pageNumber' => $request->get('page'),
  516.         ]));
  517.     }
  518.     private function getMinMaxPrices($itemsParams) {
  519.         $collection $this->getDocumentManager()->getDocumentCollection(Product::class);
  520.         $pipeline = [
  521.             ['$match' => $itemsParams['query']],
  522.             ['$group' => [
  523.                 '_id' => null,
  524.                 'minPrice' => ['$min' => '$price'],
  525.                 'maxPrice' => ['$max' => '$price'],
  526.             ]],
  527.         ];
  528.         $docs $collection->aggregate($pipeline$this->options);
  529.         $minPrice 0;
  530.         $maxPrice 0;
  531.         foreach ($docs as $doc) {
  532.             $minPrice floor($doc['minPrice']);
  533.             $maxPrice ceil($doc['maxPrice']);
  534.             break;
  535.         }
  536.         return [$minPrice$maxPrice];
  537.     }
  538.     /**
  539.      * @Route("/catalog-filter-ajax", name="catalog-filter-ajax")
  540.      * @param Request $request
  541.      * @return \Symfony\Component\HttpFoundation\Response
  542.      */
  543.     public function filterAjax(Request $request) {
  544.         $categoryId $request->get('categoryId');
  545.         $searchText $request->get('searchText');
  546.         /**
  547.          * @var Category $category
  548.          */
  549.         $category $this->getDocumentRepository(Category::class)->find($categoryId);
  550.         $params $this->getItemsParams($request$category$searchText);
  551.         return new JsonResponse([
  552.             'html' => $this->renderView('application/catalog/products.html.twig'$params),
  553.             'paginationHtml' => $this->renderView('application/catalog/pagination.html.twig'$params),
  554.             'tags' => $this->renderView('application/catalog/tags.html.twig'$params),
  555.             'filtered' => $params['filtered'],
  556.             'page' => $params['page'],
  557.         ]);
  558.     }
  559.     /**
  560.      * @Route("/product/{url}", name="catalog/item")
  561.      * @param Request $request
  562.      * @return \Symfony\Component\HttpFoundation\Response
  563.      */
  564.     public function item(Request $request$url) {
  565.         /**
  566.          * @var Product $product
  567.          */
  568.         $product $this->getDocumentRepository(Product::class)->findOneBy(['_id' => $url'active' => true]);
  569.         if (!$product) {
  570.             $product $this->getDocumentRepository(Product::class)->findOneBy(['url' => $url'active' => true]);
  571.         }
  572.         if (!$product) {
  573.             throw $this->createNotFoundException("Product with url '{$url}' does not exist or not active");
  574.         }
  575.         $this->setProductSeo($product$request->getLocale());
  576.         $category null;
  577.         if ($product->getSale()) {
  578.             $category $this->getDocumentRepository(Category::class)->findOneBy(['sale' => true]);
  579.         } else {
  580.             foreach ($product->getCategories() as $category) {
  581.                 if ($category->getActive()) {
  582.                     break;
  583.                 }
  584.             }
  585.         }
  586.         // Получаем форматированную цену с учетом настроек категории
  587.         $formattedPrice null;
  588.         if ($category) {
  589.             $formattedPrice $product->getFormattedPriceByCategory($category$this->getDocumentManager());
  590.         }
  591.         return $this->addWatched($product$request$this->render('application/catalog/product.html.twig', [
  592.             'product' => $product,
  593.             'category' => $category,
  594.             'catalogPage' => $this->getCatalogPage(),
  595.             'videos' => $product->getVideos(),
  596.             'formattedPrice' => $formattedPrice
  597.         ]));
  598.     }
  599.     private function addWatched(Product $productRequest $requestResponse $response)
  600.     {
  601.         $ids explode(';'$request->cookies->get(static::RECENTLY_WATCHED_PRODUCTS_COOKIE_NAME''));
  602.         $key array_search((string)$product->getId(), $ids);
  603.         if ($key !== false) {
  604.             unset($ids[$key]);
  605.         }
  606.         array_unshift($ids$product->getId());
  607.         if (count($ids) > self::RECENTLY_WATCHED_PRODUCTS_LIMIT) {
  608.             $ids array_slice($ids0self::RECENTLY_WATCHED_PRODUCTS_LIMIT);
  609.         }
  610.         $response->headers->setCookie(Cookie::create(static::RECENTLY_WATCHED_PRODUCTS_COOKIE_NAMEimplode(';'$ids)));
  611.         return $response;
  612.     }
  613.     /**
  614.      * @Route("/catalog-show-more", name="catalog-show-more")
  615.      * @param Request $request
  616.      * @return Response
  617.      */
  618.     public function showMore(Request $request)
  619.     {
  620.         $categoryId $request->get('categoryId');
  621.         $searchText $request->get('searchText');
  622.         /**
  623.          * @var Category $category
  624.          */
  625.         $category $this->getDocumentRepository(Category::class)->find($categoryId);
  626.         $params $this->getItemsParams($request$category$searchText);
  627.         return new JsonResponse([
  628.             'html' => $this->renderView('application/catalog/products.html.twig'$params),
  629.             'paginationHtml' => $this->renderView('application/catalog/pagination.html.twig'$params),
  630.             'page' => $params['page']
  631.         ]);
  632.     }
  633. }