src/Repository/SaleRepository.php line 129

Open in your IDE?
  1. <?php
  2. /**
  3.  * Created by PhpStorm.
  4.  * User: adv
  5.  * Date: 06.12.15
  6.  * Time: 20:00
  7.  */
  8. namespace Slivki\Repository;
  9. use Doctrine\ORM\Query;
  10. use Doctrine\ORM\EntityRepository;
  11. use Slivki\Entity\Category;
  12. use Slivki\Entity\City;
  13. use Slivki\Entity\Comment;
  14. use Slivki\Entity\Media;
  15. use Slivki\Entity\Media\MallBrandGalleryMedia;
  16. use Slivki\Entity\OfferPayedCategory;
  17. use Slivki\Entity\Sale;
  18. use Slivki\Entity\Seo;
  19. use Slivki\Entity\TopSale;
  20. use Slivki\Entity\VisitCounter;
  21. use Slivki\Util\SoftCache;
  22. use Slivki\Entity\Visit;
  23. use Slivki\Util\TarantoolCache;
  24. use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
  25. class SaleRepository extends EntityRepository {
  26.     const CACHE_NAME "Sale-0";
  27.     const CACHE_NAME_SIDEBAR 'sidebar-2-';
  28.     const CACHE_NAME_PRODUCTS 'sale-products-01-';
  29.     const GALLERY_CACHE_KEY "sales-in-gallery-2-";
  30.     const ACTIVE_SALES_BY_CATEGORY "activeByCategory";
  31.     const CACHE_NAME_TEASERS_FLIER 'food-teaser-flier';
  32.     const CACHE_NAME_GEO_LOCATION_DATA 'sale-geolocation-data';
  33.     const VISIT_COUNT_CACHE_KEY 'visit-count';
  34.     const FLIER_VISIT_COUNT_CACHE_KEY 'flier-visit-count';
  35.     const VISITED_SALE_COUNT_CACHE_KEY 'visited-sales-count';
  36.     const VISITED_FLIER_COUNT_CACHE_KEY 'visited-flier-count';
  37.     const CITIES_CACHE_KEY 'flier-cities-';
  38.     const CACHE_KEY_BY_BRAND '-brand-';
  39.     const CACHE_KEY_BY_CITY '-city-';
  40.     const CACHE_KEY_BY_CATEGORY '-category-';
  41.     const PRODUCT_TAGS_CACHE_KEY '-tags-';
  42.     const SOS_CATEGORY_ID 53;
  43.     const NEW_CATEGORY_ID 59;
  44.     const POPULAR_SALE_CATEGORY_ID 1978;
  45.     const FAKE_CATEGORY_ID PHP_INT_MAX;
  46.     const RESIZED_IMAGE_PREFIX '_resized_';
  47.     const HASWM_IMAGE_PREFIX '_haswm_';
  48.     const WITHWM_IMAGE_PREFIX '_65de6_';
  49.     const HOT_SALES_DURABILITY_DAYS 7;
  50.     const API_HOST 'http://supercheck.by';
  51.     const API_ROOT_URL '/api/v2/';
  52.     public static $salePositionList = [];
  53.     /**
  54.      * @throws NotFoundHttpException
  55.      */
  56.     public function getActiveSalesByCategoryID($categoryID) {
  57.         $softCache = new SoftCache(self::CACHE_NAME);
  58.         $saleListByCategory $softCache->get(self::ACTIVE_SALES_BY_CATEGORY);
  59.         if (!$saleListByCategory) {
  60.             throw new NotFoundHttpException();
  61.         }
  62.         $saleList = [];
  63.         if (isset($saleListByCategory[$categoryID])) {
  64.             $saleList $softCache->getMulti($saleListByCategory[$categoryID]);
  65.         }
  66.         if (!$saleList) {
  67.             return [];
  68.         }
  69.         $result = [];
  70.         foreach ($saleList as $sale) {
  71.             if ($sale != null) {
  72.                 $result[] = $sale;
  73.             }
  74.         }
  75.         $saleList $result;
  76.         $category $this->getEntityManager()->getRepository(Category::class)->findCached($categoryID);
  77.         if (in_array($categoryID, [self::POPULAR_SALE_CATEGORY_IDCategory::FLIER_SALE_CATEGORY_ID]) ||
  78.             (isset($category['category']) && $category['category']->isChildOf(Category::FLIER_SALE_CATEGORY_ID))) {
  79.             $dql "select salePosition from Slivki:OfferPayedCategory salePosition index by salePosition.entityID where salePosition.categoryID = :categoryID order by salePosition.position";
  80.             self::$salePositionList $this->getEntityManager()->createQuery($dql)->setParameter('categoryID'$categoryID)->execute();
  81.             usort($saleList, ['\Slivki\Repository\SaleRepository''compareSalesByPosition']);
  82.         } else {
  83.             usort($saleList, ['\Slivki\Repository\SaleRepository''compareSalesBySince']);
  84.         }
  85.         return $saleList;
  86.     }
  87.     public function getAllSalesByCategoryID($categoryID) {
  88.         $dql "select sale, offerPayedCategory.position, category from Slivki:Sale sale
  89.             join sale.categories category
  90.             left join Slivki:OfferPayedCategory offerPayedCategory
  91.               with offerPayedCategory.entityID = sale.ID and offerPayedCategory.categoryID = :categoryID
  92.             where category.ID = :categoryID
  93.             order by offerPayedCategory.position, sale.active desc, sale.since desc, sale.ID desc";
  94.         $saleList $this->getEntityManager()->createQuery($dql)->setParameter("categoryID"$categoryID)->getResult();
  95.         return $saleList;
  96.     }
  97.     public function getSortedActiveSalesByCategoryID($categoryID) {
  98.         $dql "select sale, offerPayedCategory.position from Slivki:Sale sale
  99.             join sale.categories category
  100.             left join Slivki:OfferPayedCategory offerPayedCategory
  101.               with offerPayedCategory.entityID = sale.ID and offerPayedCategory.categoryID = :categoryID
  102.             where category.ID = :categoryID and sale.active = true and sale.since < CURRENT_TIMESTAMP() and sale.till > CURRENT_TIMESTAMP() 
  103.             order by offerPayedCategory.position, sale.since desc, sale.ID desc";
  104.         $saleList $this->getEntityManager()->createQuery($dql)->setParameter("categoryID"$categoryID)->getResult();
  105.         return $saleList;
  106.     }
  107.     public function getAllActiveSales(){
  108.         $dql "select sale from Slivki:Sale sale 
  109.                 where sale.active = true
  110.                 and sale.since < CURRENT_TIMESTAMP() 
  111.                 and sale.till > CURRENT_TIMESTAMP() 
  112.                 order by sale.active desc";
  113.         $sales $this->getEntityManager()->createQuery($dql)->getResult();
  114.         return $sales;
  115.     }
  116.     public function getActiveSaleByID($saleID) {
  117.         $softCache = new SoftCache(self::CACHE_NAME);
  118.         $sale $softCache->get($saleID);
  119.         if ($sale && $sale->getCategories()->count() == || true) {//TODO: delete after bugfix
  120.             $sale $this->reloadCacheForSale($saleID);
  121.         }
  122.         return $sale;
  123.     }
  124.     public function reloadCache() {
  125.         $dql "select sale, category, saleVersions, geoLocations from Slivki:Sale sale index by sale.ID 
  126.           left join sale.versions saleVersions
  127.           left join sale.geoLocations geoLocations 
  128.           inner join sale.categories category where category.active = true and sale.active = true 
  129.           and sale.since < CURRENT_TIMESTAMP() and sale.till > CURRENT_TIMESTAMP() order by sale.since desc";
  130.         $saleList $this->getEntityManager()->createQuery($dql)->getResult();
  131.         $saleListByCategory = [];
  132.         $mediaRepository $this->getEntityManager()->getRepository(Media::class);
  133.         $commentRepository $this->getEntityManager()->getRepository(Comment::class);
  134.         $cityList = [];
  135.         $salesByCity = [];
  136.         /** * @var \Slivki\Entity\Sale  $sale  */
  137.         foreach ($saleList as $saleID => $sale) {
  138.             $sale->getDirectors()->toArray();
  139.             $saleList[$saleID]->setIcon($mediaRepository->getSaleIconMedia($saleID));
  140.             $saleList[$saleID]->setHotFeedIconMedia($mediaRepository->getOfferHotFeedIconMedia($saleID));
  141.             $saleList[$saleID]->setAlternativeIcon($mediaRepository->getSaleAlternativeIconMedia($saleID));
  142.             $saleList[$saleID]->setFlierMedia($mediaRepository->getFlierImageMedia($saleID));
  143.             foreach ($sale->getDescriptions() as $saleDescription) {
  144.                 $saleDescription->setDescription(str_replace(self::HASWM_IMAGE_PREFIXself::WITHWM_IMAGE_PREFIX$saleDescription->getDescription()));
  145.                 $saleDescription->getEntityDescriptionSliderImages()->toArray();
  146.             }
  147.             $saleList[$saleID]->setRating($commentRepository->getEntityRating(Category::SALE_CATEGORY_ID$saleID));
  148.             foreach ($sale->getCategories() as $category) {
  149.                 $saleListByCategory[$category->getID()][] = $sale->getID();
  150.             }
  151.         }
  152.         $saleCategoryList = [];
  153.         $categoryList $this->getEntityManager()->getRepository(Category::class)->getActiveCategoriesNotCached(Category::SALE_CATEGORY_ID);
  154.         foreach ($categoryList as $category) {
  155.             if (!isset($saleListByCategory[$category->getID()])) {
  156.                 continue;
  157.             }
  158.             $category->setEntityCount(count($saleListByCategory[$category->getID()]));
  159.             $category->getParentCategories()->toArray();
  160.             $category->setHotFeedIconMedia($mediaRepository->getСategoryHotFeedIconMedia($category->getID()));
  161.             $saleCategoryList[$category->getID()] = ['category' => $category'entityList' => $saleListByCategory[$category->getID()]];
  162.         }
  163.         $softCache = new SoftCache(self::CACHE_NAME);
  164.         $softCache->setMulti($saleList0);
  165.         $softCache->set(self::ACTIVE_SALES_BY_CATEGORY$saleListByCategory0);
  166.         $softCache = new SoftCache(CategoryRepository::CACHE_NAME);
  167.         $categoryListCached $softCache->get(CategoryRepository::ALL_CATEGORIES_CACHE_KEY);
  168.         foreach ($saleCategoryList as $categoryID => $categoryData) {
  169.             $categoryListCached[$categoryID] = $categoryData;
  170.         }
  171.         $softCache->set(CategoryRepository::ALL_CATEGORIES_CACHE_KEY$categoryListCached0);
  172.         $softCache->setMulti($saleCategoryList0);
  173.         $this->getEntityManager()->clear();
  174.         $this->reloadFliersCache($saleList);
  175.     }
  176.     /** @deprecated  */
  177.     public function reloadCacheForSale($saleID) {
  178.         /** @var Sale $sale */
  179.         $sale $this->find($saleID);
  180.         if (!$sale) {
  181.             return;
  182.         }
  183.         if (!$sale->isActive() || !$sale->isVisible()) {
  184.             $this->removeSaleFromCache($sale->getID());
  185.             return;
  186.         }
  187.         $mediaRepository $this->getEntityManager()->getRepository(Media::class);
  188.         $sale->setIcon($mediaRepository->getSaleIconMedia($sale->getID()));
  189.         $sale->setHotFeedIconMedia($mediaRepository->getOfferHotFeedIconMedia($sale->getID()));
  190.         $sale->setAlternativeIcon($mediaRepository->getSaleAlternativeIconMedia($sale->getID()));
  191.         $sale->setFlierMedia($mediaRepository->getFlierImageMedia($saleID));
  192.         $sale->getDirectors()->toArray();
  193.         $sale->getGeoLocations()->toArray();
  194.         foreach ($sale->getDescriptions() as $saleDescription) {
  195.             $saleDescription->setDescription(str_replace(self::HASWM_IMAGE_PREFIXself::WITHWM_IMAGE_PREFIX$saleDescription->getDescription()));
  196.         }
  197.         $sale->setRating($this->getEntityManager()->getRepository(Comment::class)->getEntityRating(Category::SALE_CATEGORY_ID$saleID));
  198.         foreach ($sale->getVersions()->toArray() as $version) {
  199.             foreach ($version->getDescriptions() as $description) {
  200.                 $description->getEntityDescriptionSliderImages()->toArray();
  201.             }
  202.         }
  203.         $softCache = new SoftCache(self::CACHE_NAME);
  204.         $softCache->set($saleID$sale0);
  205.         $salesByCategory $softCache->get(self::ACTIVE_SALES_BY_CATEGORY);
  206.         foreach ($salesByCategory as $categoryID => $category) {
  207.             if (in_array($saleID$category)) {
  208.                 $categoryFound false;
  209.                 foreach ($sale->getCategories() as $saleCategory) {
  210.                     if ($saleCategory->getID() == $categoryID) {
  211.                         $categoryFound true;
  212.                         break;
  213.                     }
  214.                 }
  215.                 if (!$categoryFound) {
  216.                     $salesByCategory[$categoryID] = array_diff($salesByCategory[$categoryID], [$saleID]);
  217.                 }
  218.             }
  219.         }
  220.         foreach ($sale->getCategories() as $category) {
  221.             if (!isset($salesByCategory[$category->getID()])) {
  222.                 if ($category->isActive() || !$category->isPast()) {
  223.                     $category->getBanners()->toArray();
  224.                     $category->setEntityCount(1);
  225.                     $categoryForCache['category'] = $category;
  226.                     $categoryForCache['entityList'][] = $saleID;
  227.                     $salesByCategory[$category->getID()][] = $saleID;
  228.                     $this->getEntityManager()->getRepository(Category::class)->putCategoryToCache($categoryForCache);
  229.                 }
  230.             } else {
  231.                 if (!in_array($sale->getID(), $salesByCategory[$category->getID()])) {
  232.                     $salesByCategory[$category->getID()][] = $sale->getID();
  233.                 }
  234.             }
  235.         }
  236.         $softCache->set(self::ACTIVE_SALES_BY_CATEGORY$salesByCategory0);
  237.         $this->getEntityManager()->getRepository(Seo::class)->reloadCacheForEntity(SeoRepository::RESOURCE_URL_SALE_DETAILS$saleID);
  238.         return $sale;
  239.     }
  240.     public function reloadFliersCache($flierList null) {
  241.         if (!$flierList) {
  242.             $flierList $this->getActiveSalesByCategoryID(Category::FLIER_SALE_CATEGORY_ID);
  243.         }
  244.         $cityList = [];
  245.         foreach ($flierList as $flier) {
  246.             if ($flier->isInCategory(Category::FLIER_SALE_CATEGORY_ID)) {
  247.                 foreach ($flier->getGeoLocations() as $geoLocation) {
  248.                     $cityList[trim($geoLocation->getCity())][] = $flier->getID();
  249.                 }
  250.             }
  251.         }
  252.         ksort($cityList);
  253.         $cityListSorted = [];
  254.         foreach ($cityList  as $cityName => $fliers) {
  255.             $cityListSorted[mb_substr($cityName01)][] = ['cityName' => $cityName'entityList' => $fliers];
  256.         }
  257.         $softCache = new SoftCache(self::CACHE_NAME);
  258.         $softCache->set(self::CITIES_CACHE_KEY$cityListSorted0);
  259.     }
  260.     public function removeSaleFromCache($saleID) {
  261.         $softCache = new SoftCache(self::CACHE_NAME);
  262.         $sale $softCache->get($saleID);
  263.         if (!$sale) {
  264.             return;
  265.         }
  266.         $softCache->delete($saleID);
  267.         $salesByCategory $softCache->get(self::ACTIVE_SALES_BY_CATEGORY);
  268.         foreach ($salesByCategory as $key => $category) {
  269.             $salesByCategory[$key] = array_diff($category, [$saleID]);
  270.         }
  271.         $softCache->set(self::ACTIVE_SALES_BY_CATEGORY$salesByCategory0);
  272.     }
  273.     private static function compareSalesBySince(Sale $saleSale $sale1) {
  274.         return $sale->getSince() > $sale1->getSince() ? -1;
  275.     }
  276.     private static function compareSalesByPosition(Sale $saleSale $sale1) {
  277.         $saleID $sale->getID();
  278.         $sale1ID $sale1->getID();
  279.         $salePosition = isset(self::$salePositionList[$saleID]) ? self::$salePositionList[$saleID]->getPosition() : INF;
  280.         $sale1Position = isset(self::$salePositionList[$sale1ID]) ? self::$salePositionList[$sale1ID]->getPosition() : INF;
  281.         if ($salePosition == $sale1Position) {
  282.             if ($sale->getSince() == $sale1->getSince()) {
  283.                 return $sale->getID() > $sale1->getID() ? -1;
  284.             }
  285.             return $sale->getSince() > $sale1->getSince() ? -1;
  286.         }
  287.         return $salePosition $sale1Position : -1;
  288.     }
  289.     
  290.     public function getCategoryBoxData($categoryID) {
  291.         $categoryRepository $this->getEntityManager()->getRepository(Category::class);
  292.         $categoryList $categoryRepository->getActiveCategories(0, array(Category::DEFAULT_CATEGORY_TYPE), Category::SALE_CATEGORY_ID);
  293.         foreach ($categoryList as $category) {
  294.             $saleList null;
  295.             if ($category->getID() == $categoryID) {
  296.                 $saleList $this->getSalesSorted($categoryID);
  297.             }
  298.             $data['categoryList'][$category->getID()] = [
  299.                 "category" => $category,
  300.                 "saleList" => $saleList
  301.             ];
  302.         }
  303.         return $data;
  304.     }
  305.     public function getCategoryBoxDataCached($categoryID$reload false) {
  306.         $softCache = new SoftCache(self::CACHE_NAME);
  307.         $cacheKey 'categoryBox-17-' $categoryID;
  308.         $categoryBoxData $softCache->get($cacheKey);
  309.         if ($categoryBoxData && !$reload) {
  310.             return $categoryBoxData;
  311.         }
  312.         $categoryBoxData $this->getCategoryBoxData($categoryID);
  313.         $softCache->set($cacheKey$categoryBoxData0);
  314.         return $categoryBoxData;
  315.     }
  316.     
  317.     public function getSalesSorted($categoryID) {
  318.         $saleList $this->getActiveSalesByCategoryID($categoryID);
  319.         $payedPositions $this->getEntityManager()->getRepository(OfferPayedCategory::class)->findBy(
  320.             ['categoryID' => $categoryID], ['position' => 'asc']);
  321.         if ($payedPositions) {
  322.             foreach ($payedPositions as $position) {
  323.                 foreach ($saleList as $key => $sale) {
  324.                     if ($sale->getID() == $position->getEntityID()) {
  325.                         unset($saleList[$key]);
  326.                         array_splice($saleList$position->getPosition() - 10, [$sale]);
  327.                         break;
  328.                     }
  329.                 }
  330.             }
  331.         }
  332.         return $saleList;
  333.     }
  334.     public function getActiveSalesCount() {
  335.         $sql "select count(*) from sale where active and now() between since and till";
  336.         return $this->getEntityManager()->getConnection()->executeQuery($sql)->fetchColumn(0);
  337.     }
  338.     public function getActiveSalesCountCached() {
  339.         $softCache = new SoftCache(self::CACHE_NAME);
  340.         $cacheKey 'activeCount';
  341.         $salesCount $softCache->get($cacheKey);
  342.         if (!$salesCount) {
  343.             $salesCount $this->getActiveSalesCount();
  344.             $softCache->set($cacheKey$salesCount30 60);
  345.         }
  346.         return $salesCount;
  347.     }
  348.     public function getTopMainSalesJson() {
  349.         $salesList $this->getEntityManager()->getRepository(Sale::class)->findBy(['showOnMainPage' => true]);
  350.         $salesListJson = [];
  351.         foreach ($salesList as $sale) {
  352.             $tempSale = [];
  353.             $tempSale['id'] = $sale->getId();
  354.             $tempSale['topPosition'] = $sale->getTopPosition();
  355.             $salesListJson[] = $tempSale;
  356.         }
  357.         return $salesListJson;
  358.     }
  359.     public function getTopMainSales() {
  360.         $salesListJson $this->getEntityManager()->getRepository(Sale::class)->findBy(['showOnMainPage' => true], ['topPosition' => 'asc']);
  361.         return $salesListJson;
  362.     }
  363.     public function getMainSales($cityID$isMobileDevice false$offset 0) {
  364.         $topSaleList $this->getEntityManager()->getRepository(TopSale::class)->findBy(
  365.             ['city' => $cityID],
  366.             ['sortOrder' => 'ASC'],
  367.             3,
  368.             $offset
  369.         );
  370.         if (count($topSaleList) == && $cityID != City::DEFAULT_CITY_ID) {
  371.             $topSaleList $this->getEntityManager()->getRepository(TopSale::class)->findBy(
  372.                 ['city' => City::DEFAULT_CITY_ID],
  373.                 ['sortOrder' => 'ASC'],
  374.                 3,
  375.                 $offset
  376.             );
  377.         }
  378.         $result = [];
  379.         $seoRepository $this->getEntityManager()->getRepository(Seo::class);
  380.         foreach ($topSaleList as $topSale) {
  381.             $sale $topSale->getSale();
  382.             $data = [];
  383.             $data['media'] = $this->getEntityManager()->getRepository(Media::class)->getSaleMainIconMedia($sale->getID());
  384.             $data['sale'] = $sale;
  385.             if ($sale->getAlternativeURL()) {
  386.                 $data['url'] = $sale->getAlternativeURL();
  387.             } else {
  388.                 $seo $seoRepository->getByEntity(SeoRepository::RESOURCE_URL_SALE_DETAILS$sale->getID());
  389.                 $data['url'] = $seo $seo->getMainAlias() : '';
  390.             }
  391.             $result[] = $data;
  392.         }
  393.         return $result;
  394.     }
  395.     /**
  396.      * @return Sale
  397.      */
  398.     public function findCached($saleID) {
  399.         $softCache = new SoftCache(self::CACHE_NAME);
  400.         return $softCache->get($saleID);
  401.     }
  402.     public function getVisitCount($sale) {
  403.         $dql "select count(visit.ID) from Slivki:Visit visit 
  404.             where visit.entityID = :saleID and visit.entityTypeID = :entityTypeID and visit.createdOn > :dateFrom and visit.createdOn > :saleSince";
  405.         return $this->getEntityManager()->createQuery($dql)
  406.             ->setParameter('dateFrom', new \DateTime('-30 days'))
  407.             ->setParameter('saleID'$sale->getID())
  408.             ->setParameter('saleSince'$sale->getSince())
  409.             ->setParameter('entityTypeID'Category::SALE_CATEGORY_ID)
  410.             ->getSingleScalarResult();
  411.     }
  412.     public function getRecentVisitCount($saleID) {
  413.         $sql "select * from offer_category_sale where sale_id = $saleID limit 1";
  414.         $result $this->getEntityManager()->getConnection()->executeQuery($sql)->fetchAll();
  415.         return $result[0]['recent_visit_count'];
  416.     }
  417.     public function getSaleRating($saleID) {
  418.         $dql "select avg(comment.rating) rating from Slivki:Comment comment 
  419.           where comment.entityID = :entityID and comment.typeID = :typeID and comment.rating > 0 and comment.checked = true and comment.confirmedPhone = true and comment.hidden != true";
  420.         $rating $this->getEntityManager()->createQuery($dql)
  421.             ->setParameter('entityID'$saleID)->setParameter('typeID'Comment::TYPE_SALE_COMMENT)->getResult(Query::HYDRATE_SCALAR);
  422.         return round($rating[0]['rating'], 1);
  423.     }
  424.     public function sortSaleListByRecentPopularity($saleList) {
  425.         if (count($saleList) == 0) {
  426.             return [];
  427.         }
  428.         $idList = [];
  429.         foreach ($saleList as $sale) {
  430.             $idList[] = $sale->getID();
  431.         }
  432.         $sql 'select entity_id from visit_counter where type = ' VisitCounter::TYPE_SALE ' and entity_id in (' implode(', '$idList) . ') order by visit_count_recent desc';
  433.         $idList $this->getEntityManager()->getConnection()->executeQuery($sql)->fetchAll();
  434.         $saleListSorted = [];
  435.         foreach ($idList as $item) {
  436.             $saleListSorted[] = $saleList[$item['entity_id']];
  437.         }
  438.         $notInsertedSales array_udiff($saleListSorted$saleList,
  439.             function ($sale$sale1) {
  440.                 return $sale->getID() - $sale1->getID();
  441.             }
  442.         );
  443.         if (count($notInsertedSales) > 0) {
  444.             $saleListSorted array_merge($saleListSorted$notInsertedSales);
  445.         }
  446.         return $saleListSorted;
  447.     }
  448.     /**
  449.      * @return Sale
  450.      */
  451.     public function getInactiveSaleByUrl($url) {
  452.         $sql "select sale.id from sale inner join seo on sale.id = seo.entity_id where seo.resource_url = 'Slivki:Sale:details' and seo.main_alias = '$url' and sale.till < now() limit 1";
  453.         $saleID $this->getEntityManager()->getConnection()->executeQuery($sql)->fetchColumn();
  454.         if (!$saleID) {
  455.             return null;
  456.         }
  457.         return $this->find($saleID);
  458.     }
  459.     public function getHotSales($limit null$offset 0) {
  460.         $sql "select sale.id from sale inner join category2entity on sale.id = category2entity.entity_id
  461.           where category_id = " Category::NEW_SALE_CATEGORY_ID .  " and sale.active and sale.since > now() - interval '" self::HOT_SALES_DURABILITY_DAYS "' day order by sale.since desc, sale.id desc offset " $offset;
  462.         if ($limit) {
  463.             $sql .= ' limit ' $limit;
  464.         }
  465.         $salIDList $this->getEntityManager()->getConnection()->executeQuery($sql)->fetchAll(\PDO::FETCH_COLUMN);
  466.         $softCache = new SoftCache(self::CACHE_NAME);
  467.         $saleList $softCache->getMulti($salIDList);
  468.         if (!$saleList) {
  469.             $saleList = [];
  470.         }
  471.         $saleListFiltered = [];
  472.         foreach ($saleList as $sale) {
  473.             if ($sale) {
  474.                 $saleListFiltered[] = $sale;
  475.             }
  476.         }
  477.         return $saleListFiltered;
  478.     }
  479.     public function getAllActiveSalesCached() {
  480.         $categoryList $this->getEntityManager()->getRepository(Category::class)->getActiveCategories(0,[Category::DEFAULT_CATEGORY_TYPE], Category::SALE_CATEGORY_ID);
  481.         $saleList = [];
  482.         foreach ($categoryList as $category) {
  483.             foreach ($this->getActiveSalesByCategoryID($category->getID()) as $sale) {
  484.                 if (!isset($saleList[$sale->getID()])) {
  485.                     $saleList[$sale->getID()] = $sale;
  486.                 }
  487.             }
  488.         }
  489.         return $saleList;
  490.     }
  491.     public function getSaleIDSorted($sortBy$categoryID null$offset null$limit null, array $coordinates null) {
  492.         $sql 'select sale.id from sale';
  493.         $categoryCondition '';
  494.         if ($categoryID) {
  495.             $sql .= ' inner join category2entity on sale.id = category2entity.entity_id';
  496.             $categoryCondition ' and category2entity.category_id = ' . (int)$categoryID;
  497.         }
  498.         $orderBy ' order by ';
  499.         switch ($sortBy) {
  500.             case 'new':
  501.                 $orderBy .= 'sale.since desc';
  502.                 break;
  503.             case 'views':
  504.                 $sql .= ' inner join (select entity_id, sum(visit_count) as visit_count from stat_visit_count_aggregated where entity_type_id = ' Visit::TYPE_SALE .
  505.                     " and visit_date > now() + '-45 days' group by 1) as visit_count on sale.id = visit_count.entity_id";
  506.                 $orderBy .= 'visit_count.visit_count desc';
  507.                 break;
  508.             case 'location':
  509.                 if ($coordinates) {
  510.                     $sql .= ' inner join geo_location_relation on sale.id = geo_location_relation.entity_id inner join geo_location on geo_location_relation.location_id = geo_location.id';
  511.                     $orderBy .= "min(earth_distance(ll_to_earth($coordinates[0]$coordinates[1]), ll_to_earth(coalesce(nullif(latitude, ''), '0')::float8, coalesce(nullif(longitude, ''), '0')::float8)))";
  512.                 }
  513.                 break;
  514.             default:
  515.                 $orderBy .= 'sale.since desc';
  516.                 break;
  517.         }
  518.         $sql .= ' where sale.active and now() between sale.since and sale.till' $categoryCondition;
  519.         if ($coordinates) {
  520.             $sql .= ' group by 1 ';
  521.         }
  522.         $sql .= $orderBy;
  523.         if ($offset !== null && $limit) {
  524.             $sql .= ' offset ' . (int)$offset ' limit ' . (int)$limit;
  525.         }
  526.         return $this->getEntityManager()->getConnection()->executeQuery($sql)->fetchAll(\PDO::FETCH_COLUMN);
  527.     }
  528.     public function getSalesWithProduct() {
  529.         $saleList $this->getActiveSalesByCategoryID(Category::FLIER_SALE_CATEGORY_ID);
  530.         $result = [];
  531.         foreach ($saleList as $sale) {
  532.             if ($sale->getSaleProducts()->count() > 0) {
  533.                 $result[] = $sale;
  534.             }
  535.         }
  536.         return $result;
  537.     }
  538.     public function getProducts() {
  539.         $dql 'select product from Slivki:Product product join product.saleProducts saleProduct join saleProduct.sale sale where sale.active = true and sale.till > now() order by product.name asc';
  540.         return $this->getEntityManager()->createQuery($dql)->getResult();
  541.     }
  542.     public function getSaleProducts($offset$limit null) {
  543.         $dql 'select product, saleProduct, saleVersion, sale, product, teaserMedia from Slivki:Product product join product.saleProducts saleProduct join saleProduct.saleVersion saleVersion join saleVersion.sale sale index by saleProduct.ID
  544.            join saleProduct.teaserMedia teaserMedia  
  545.            where sale.active = true and sale.till > current_timestamp() and current_timestamp() between saleVersion.since and saleVersion.till + \'1 day\' order by sale.till desc, saleProduct.ID asc';
  546.         $query $this->getEntityManager()->createQuery($dql);
  547.         $query->setFirstResult($offset);
  548.         if ($limit) {
  549.             $query->setMaxResults($limit);
  550.         }
  551.         return $query->getResult();
  552.     }
  553.     public function getSaleProductCategories() {
  554.         $dql 'select category from Slivki:ProductCategory category join category.products product join product.saleProducts saleProduct join saleProduct.saleVersion saleVersion
  555.           join saleVersion.sale sale where sale.active = true order by category.name';
  556.         return $this->getEntityManager()->createQuery($dql)->getResult();
  557.     }
  558.     public function getSaleProductTopLevelCategories() {
  559.         $dql 'select category from Slivki:ProductCategory category where category.parents is empty order by category.name';
  560.         return $this->getEntityManager()->createQuery($dql)->getResult();
  561.     }
  562.     public function getMallBrandRelationForVideoGuide(Sale $sale) {
  563.         $videoGuideYoutubeID $sale->getVideoGuideYoutubeID();
  564.         if (!$videoGuideYoutubeID || $videoGuideYoutubeID == '') {
  565.             return null;
  566.         }
  567.         $entityManager $this->getEntityManager();
  568.         /** @var MallBrandGalleryMedia $media */
  569.         $media $entityManager->getRepository(MallBrandGalleryMedia::class)->findByName($videoGuideYoutubeID);
  570.         if (!isset($media[0])) {
  571.             return null;
  572.         }
  573.         return $media[0]->getMallBrandRelation();
  574.     }
  575.     public function reloadProductCache() {
  576.         ini_set('memory_limit''1g');
  577.         $dbConnection $this->getEntityManager()->getConnection();
  578.         $productList $this->getSaleProducts(0);
  579.         $byTradingNetwork = [];
  580.         $byCategory = [];
  581.         $byCity = [];
  582.         $saleProductList = [];
  583.         $tagList = [];
  584.         foreach ($productList as $product) {
  585.             foreach ($product->getSaleProducts() as $saleProduct) {
  586.                 if (!$saleProduct->getProduct()->getCategories()) {
  587.                     continue;
  588.                 }
  589.                 $saleProductList[$saleProduct->getID()] = $saleProduct;
  590.             }
  591.         }
  592.         $sql 'select product.id, product.name from product inner join sale_product on product.id = sale_product.product_id 
  593.           inner join sale_version on sale_product.sale_version_id = sale_version.id';
  594.         $allProductList $this->getEntityManager()->getConnection()->executeQuery($sql)->fetchAll(\PDO::FETCH_ASSOC);
  595.         foreach ($allProductList as $item) {
  596.             $position mb_stripos($item['name'], '"');
  597.             if ($position 0) {
  598.                 $tag trim(mb_strtolower(mb_substr($item['name'], 0$position)));
  599.                 $tagList[$tag][] = $item['id'];
  600.             }
  601.         }
  602.         foreach ($saleProductList as $key => $saleProduct) {
  603.             $sale $saleProduct->getSaleVersion()->getSale();
  604.             $sale->getGeoLocations()->toArray();
  605.             $saleProduct->getProduct();
  606.             $sale->getDirectors()->first();
  607.             $saleProductList[$key] = $saleProduct;
  608.             foreach ($saleProduct->getProduct()->getCategories() as $category) {
  609.                 $category->getProducts()->toArray();
  610.             }
  611.             $byTradingNetwork[$sale->getID()][] = $saleProduct->getID();
  612.             $sql 'select parent_id from category2entity inner join category_child_relation on category2entity.category_id = category_child_relation.child_id where entity_id = ' $saleProduct->getProduct()->getID();
  613.             foreach ($dbConnection->executeQuery($sql)->fetchAll(\PDO::FETCH_COLUMN) as $categoryID) {
  614.                 $byCategory[$categoryID][] = $saleProduct->getID();
  615.             }
  616.             foreach ($sale->getGeoLocations() as $location) {
  617.                 $byCity[mb_strtolower($location->getCity())][] = $saleProduct->getID();
  618.             }
  619.         }
  620.         $softCache = new SoftCache(self::CACHE_NAME_PRODUCTS);
  621.         $softCache->setMulti($saleProductList0);
  622.         $softCache->set(SoftCache::CACHE_KEY_ALL$saleProductList0);
  623.         $softCache->set(self::CACHE_KEY_BY_BRAND$byTradingNetwork0);
  624.         $softCache->set(self::CACHE_KEY_BY_CATEGORY$byCategory0);
  625.         $softCache->set(self::CACHE_KEY_BY_CITY$byCity0);
  626.         $softCache->set(self::PRODUCT_TAGS_CACHE_KEY$tagList0);
  627.         dump('done');
  628.     }
  629.     public function getSaleProductsCached() {
  630.         return (new SoftCache(self::CACHE_NAME_PRODUCTS))->get(SoftCache::CACHE_KEY_ALL);
  631.     }
  632.     public function getSaleProductCached($saleProductID) {
  633.         return (new SoftCache(self::CACHE_NAME_PRODUCTS))->get($saleProductID);
  634.     }
  635.     public function getVideoguideAutorCategory(Sale $sale) {
  636.         /** @var Category $category */
  637.         foreach ($sale->getCategories() as $category) {
  638.             if ($category->isChildOf(Category::SALE_VIDEO_GUIDE_AUTORS_CATEGORY_ID)) {
  639.                 return $category;
  640.             };
  641.         }
  642.         return null;
  643.     }
  644.     public function getSaleProductsIDByCategoryID($categoryID) {
  645.         $sql 'select DISTINCT sale_product.id from sale_product
  646.                 inner join sale_version on sale_product.sale_version_id = sale_version.id
  647.                 inner join category2entity on sale_version.sale_id = category2entity.entity_id
  648.                 inner join sale on category2entity.entity_id = sale.id
  649.                 inner join media on media.entity_id = sale_product.id
  650.                 where category2entity.category_id = ' $categoryID ' and sale.active and media.type = 1
  651.                 and now() between sale_version.since and sale_version.till order by 1;';
  652.         return $this->getEntityManager()->getConnection()->executeQuery($sql)->fetchAll(\PDO::FETCH_COLUMN);
  653.     }
  654.     public function getSaleProductsByCategoryID($categoryID) {
  655.         $dql 'select saleProduct, saleVersion from Slivki:SaleProduct saleProduct join saleProduct.saleVersion saleVersion join saleProduct.product product join product.categories category 
  656.             where category.ID = :categoryID and current_date() between saleVersion.since and saleVersion.till';
  657.         $query $this->getEntityManager()->createQuery($dql)->setParameter('categoryID',$categoryID);
  658.         return $query->getResult();
  659.     }
  660.     public function getSupercheckShops() {
  661.         $shopList $this->sendSupercheckApiRequest('city/0/shops'falsefalsenull);
  662.         if (!$shopList) {
  663.             return [];
  664.         }
  665.         return $shopList;
  666.     }
  667.     private function sendSupercheckApiRequest($request$body false$userToken false$httpHeader 'json') {
  668.         $request self::API_HOST  self::API_ROOT_URL $request;
  669.         $curl curl_init($request);
  670.         curl_setopt($curlCURLOPT_RETURNTRANSFERtrue);
  671.         $result curl_exec($curl);
  672.         $statusCode curl_getinfo($curlCURLINFO_HTTP_CODE);
  673.         $result json_decode($result);
  674.         if ($statusCode != 200) {
  675.             return false;
  676.         }
  677.         return $result->result;
  678.     }
  679. }