src/Repository/OfferRepository.php line 782

Open in your IDE?
  1. <?php
  2. namespace Slivki\Repository;
  3. use Doctrine\ORM\EntityRepository;
  4. use Doctrine\ORM\Query;
  5. use Doctrine\ORM\QueryBuilder;
  6. use League\Period\Period;
  7. use Slivki\Entity\Banner;
  8. use Slivki\Entity\CacheReloadScheduler;
  9. use Slivki\Entity\Category;
  10. use Slivki\Entity\City;
  11. use Slivki\Entity\Director;
  12. use Slivki\Entity\EntityDescription;
  13. use Slivki\Entity\GeoLocation;
  14. use Slivki\Entity\Media;
  15. use Slivki\Entity\Offer;
  16. use Slivki\Entity\OfferExtension;
  17. use Slivki\Entity\OfferPayedCategory;
  18. use Slivki\Entity\PurchaseCount;
  19. use Slivki\Entity\OfferOrder;
  20. use Slivki\Entity\Sale;
  21. use Slivki\Entity\Seo;
  22. use Slivki\Entity\SiteSettings;
  23. use Slivki\Entity\UserGroup;
  24. use Slivki\Entity\Visit;
  25. use Slivki\Exception\OfferNotFoundException;
  26. use Slivki\Services\ImageService;
  27. use Slivki\Util\Logger;
  28. use Slivki\Util\SoftCache;
  29. use Slivki\Entity\User;
  30. use Slivki\Entity\EntityOption;
  31. use Symfony\Component\Config\Definition\Exception\Exception;
  32. class OfferRepository extends EntityRepository {
  33.     const CACHE_NAME "offers";
  34.     const CACHE_NAME_TEASERS 'offers-teasers';
  35.     const CACHE_NAME_TEASERS_MOBILE 'teasers-mobile';
  36.     const LOCKED_KEY "lock";
  37.     const SORTED_OFFERS_CACHE_KEY "sorted-offers-list-";
  38.     const SORTED_OFFERS_CACHE_NAME "sortedOffersByPosition";
  39.     const SORT_MODE_DEFAULT 'po-promokodam';
  40.     const SORT_MODE_BY_VISIT 'po-prosmotram';
  41.     const SORT_MODE_BY_RATING 'po-ocenkam';
  42.     const COMPANIES_RATING_DATA_CACHE_KEY 'company-rating-data-';
  43.     const OFFER_POSITIONS_CACHE_KEY 'positions';
  44.     const OFFER_LOCATIONS_CACHE_KEY 'locations';
  45.     const FILTER_DATA_CACHE_KEY 'filterData';
  46.     const NEW_SUBCATEGORY_CACHE_KEY '-new';
  47.     const RESIZED_IMAGE_PREFIX '_resized_';
  48.     const CACHE_NAME_GEO_LOCATION_DATA 'offer-geolocation-data';
  49.     const OFFER_CITIES_CACHE_NAME 'offer-cities';
  50.     private static $purchaseCountListByCategory;
  51.     private static $purchaseCountList;
  52.     private static $allOffersList;
  53.     private static $offerRatingList;
  54.     private static $offersOnlyPayedMonthlyPurchaseCount;
  55.     /**
  56.      * @throws OfferNotFoundException
  57.      */
  58.     public function getById(int $offerId): Offer
  59.     {
  60.         $queryBuilder $this->createQueryBuilder('offer');
  61.         $expr $queryBuilder->expr();
  62.         $offer $queryBuilder
  63.             ->andWhere($expr->eq('offer.ID'':offerId'))
  64.             ->setParameter('offerId'$offerId)
  65.             ->getQuery()
  66.             ->getOneOrNullResult();
  67.         if (!$offer instanceof Offer) {
  68.             throw OfferNotFoundException::missingId($offerId);
  69.         }
  70.         return $offer;
  71.     }
  72.     public function getActiveOffersByCategoryID($categoryID) {
  73.         ini_set('memory_limit''4g');
  74.         $softCache = new SoftCache(self::CACHE_NAME);
  75.         $category $this->getEntityManager()->getRepository(Category::class)->findCached($categoryID);
  76.         $offerList = [];
  77.         if(isset($category['entityList'])) {
  78.             $offerList $softCache->getMulti($category['entityList']);
  79.         }
  80.         if (!$offerList) {
  81.             return [];
  82.         }
  83.         $offerListCached = [];
  84.         foreach ($offerList as $key => $offer) {
  85.             if ($offer) {
  86.                 $offerListCached[$key] = $offer;
  87.             }
  88.         }
  89.         return $offerListCached;
  90.     }
  91.     public function getAllActiveOffersCached() {
  92.         $entityList = [];
  93.         $categoryList $this->getEntityManager()->getRepository(Category::class)->getAllActiveCategoriesFromCache();
  94.         foreach ($categoryList as $category) {
  95.             if ($category['category']->getDomainObjectID() == Category::OFFER_CATEGORY_ID) {
  96.                 if (isset($category['entityList']) && is_array($category['entityList'])) {
  97.                     $entityList array_merge($entityList$category['entityList']);
  98.                 } else {
  99.                     Logger::instance('getAllActiveOffersCached')->info('Category ' $category['category']->getID() . ' entityList not array');
  100.                 }
  101.             }
  102.         }
  103.         $entityList array_unique($entityList);
  104.         $softCache = new SoftCache(self::CACHE_NAME);
  105.         $offerList $softCache->getMulti($entityList);
  106.         return $offerList;
  107.     }
  108.     public function setAllOffersList() {
  109.         self::$allOffersList $this->getAllActiveOffersNoCache();
  110.     }
  111.     public function getAllActiveOffersNoCache(){
  112.         $dql "select offer from Slivki:Offer offer
  113.                 where offer.active = true
  114.                 and offer.activeSince < CURRENT_TIMESTAMP() 
  115.                 and offer.activeTill > CURRENT_TIMESTAMP()
  116.                 order by offer.active desc";
  117.         $offers $this->getEntityManager()->createQuery($dql)->getResult();
  118.         return $offers;
  119.     }
  120.     public function getActiveOffersByCategoryIDNoCache($categoryID) {
  121.         $dql "select offer from Slivki:Offer offer
  122.             join offer.categories category
  123.             where category.ID = :categoryID
  124.               and offer.active = true
  125.               and offer.activeSince < CURRENT_TIMESTAMP()
  126.               and offer.activeTill > CURRENT_TIMESTAMP()
  127.             order by offer.active desc";
  128.         $offerList $this->getEntityManager()->createQuery($dql)->setParameter("categoryID"$categoryID)->getResult();
  129.         return $offerList;
  130.     }
  131.     public function getAdvOfferList() {
  132.         return $this->getAllActiveOffersNoCache();
  133.     }
  134.     public function getAllOffersByCategoryID($categoryID) {
  135.         $dql "select offer from Slivki:Offer offer join offer.categories category where category.ID = :categoryID order by offer.active desc";
  136.         $offerList $this->getEntityManager()->createQuery($dql)->setParameter("categoryID"$categoryID)->getResult();
  137.         return $offerList;
  138.     }
  139.     public function getCountActiveOffersByCategory(Category $category) {
  140.         $offerList $this->getActiveOffersByCategoryID($category->getID());
  141.         if (!$offerList) {
  142.             return 0;
  143.         }
  144.         return count($offerList);
  145.     }
  146.     /**
  147.      * @return boolean
  148.      */
  149.     public function isOfferFreeForUser(Offer $offerUser $user null) {
  150.         if (!$offer->isInFreeCodesCategory()) {
  151.             return false;
  152.         }
  153.         if ($offer->isFree() || !$user || $user->getID() == User::FAKE_USER_ID) {
  154.             return true;
  155.         }
  156.         $orderStatus OfferOrder::STATUS_CONFIRM_FREE_IMPLICITLY;
  157.         $dql "select count(orderDetails) as amount from Slivki:OfferOrder offerOrder join offerOrder.offerOrderDetails orderDetails
  158.         where offerOrder.userID = :userID and offerOrder.offerID = :offerID and offerOrder.status = :status and offerOrder.createdOn between :from and :to";
  159.         $query $this->getEntityManager()->createQuery($dql);
  160.         $query->setParameter('userID'$user->getID());
  161.         $query->setParameter('offerID'$offer->getID());
  162.         $query->setParameter('status'$orderStatus);
  163.         $query->setParameter("from"date_format(new \DateTime(), 'Y-m-d 00:00:00'));
  164.         $query->setParameter("to"date_format(new \DateTime(), 'Y-m-d 23:59:59'));
  165.         $result $query->getOneOrNullResult(Query::HYDRATE_SCALAR);
  166.         if ($offer->getID() == Offer::PETROL_OFFER_ID) {
  167.             return $result['amount'] < 2;
  168.         } else {
  169.             return $result['amount'] == 0;
  170.         }
  171.     }
  172.     /**
  173.      * @return \Slivki\Entity\Offer
  174.      * @deprecated
  175.      */
  176.     public function findCached($offerID) {
  177.         $softCache = new SoftCache(self::CACHE_NAME);
  178.         return $softCache->get($offerID);
  179.     }
  180.     /**
  181.      * @return \Slivki\Entity\Offer|null
  182.      * @deprecated
  183.      */
  184.     public function getAnyWay($offerID) {
  185.         $offer $this->findCached($offerID);
  186.         if (!$offer) {
  187.             $offer $this->find($offerID);
  188.         }
  189.         return $offer;
  190.     }
  191.     public function getUsedCodesCount($offerID) {
  192.         $dql "select count(details) from  Slivki:OfferOrderDetails details where details.offerOrderID=:offerID";
  193.         $query $this->getEntityManager()->createQuery($dql)->setParameter("offerID"$offerID);
  194.         $usedCodesCount $query->getSingleScalarResult();
  195.         return $usedCodesCount;
  196.     }
  197.     public function getOfferMonthlyPurchaseCount($offerID$days 30) {
  198.         if ($days == 30) {
  199.             $sql "select purchase_count_last_month_with_correction from purchase_count where entity_id = " $offerID;
  200.             return $this->getEntityManager()->getConnection()->executeQuery($sql)->fetchColumn();
  201.         }
  202.         $sql "select count(*) from offer_code inner join offer_order on offer_code.offer_order_id = offer_order.id
  203.             where offer_order.offer_id = $offerID and offer_code.created_on > (now() + '- $days days')";
  204.         return $this->getEntityManager()->getConnection()->executeQuery($sql)->fetchColumn();
  205.     }
  206.     public function getOfferList($ids) {
  207.         $softCache = new SoftCache(self::CACHE_NAME);
  208.         $offerList =  $softCache->getMulti($ids);
  209.         if (!$offerList) {
  210.             return [];
  211.         }
  212.         return $offerList;
  213.     }
  214.     public function getCodeCost(Offer $offer null$codesCount null)
  215.     {
  216.         if ($offer) {
  217.             $offerId $offer->getID();
  218.             if ($offerId === 283793) {
  219.                 return 0;
  220.             }
  221.         }
  222.         $siteSettingsRepository $this->getEntityManager()->getRepository(SiteSettings::class);
  223.         if (!$offer) {
  224.             return $siteSettingsRepository->getSiteSettingsCached()->getCodeCost();
  225.         }
  226.         if ($offer->getCodeCostByCount()) {
  227.             $codeCost null;
  228.             foreach ($offer->getCodeCostByCount() as $count => $cost) {
  229.                 if ($count $codesCount) {
  230.                     break;
  231.                 }
  232.                 $codeCost $cost;
  233.             }
  234.             return $codeCost;
  235.         }
  236.         return $offer->getCodeCost() ?? $siteSettingsRepository->getSiteSettingsCached()->getCodeCost();
  237.     }
  238.     public function getOfferExtensionCodeCost(OfferExtension $extension) {
  239.         if ($extension->getCodePrice() != null) {
  240.             return $extension->getCodePrice();
  241.         }
  242.         return $this->getCodeCost($extension->getOffer());
  243.     }
  244.     /** @deprecated  */
  245.     public function putOfferToCache(Offer $offer) {
  246.         $softCache = new SoftCache(self::CACHE_NAME);
  247.         $cacheKey $offer->getID() . self::LOCKED_KEY;
  248.         while (!$softCache->add($cacheKeyself::LOCKED_KEY60));
  249.         $softCache->set($offer->getID(), $offer0);
  250.         $softCache->set($cacheKeynull1);
  251.     }
  252.     public function reloadCache() {
  253.         $dql "select offer, descriptions, offerCodePools, geoLocations, category from Slivki:Offer offer index by offer.ID inner join offer.categories category
  254.           left join offer.descriptions as descriptions left join offer.offerCodePools offerCodePools left join offer.geoLocations as geoLocations  
  255.           where category.active = true and offer.active = true and CURRENT_TIMESTAMP() between offer.activeSince and offer.activeTill
  256.           and offer.hidden = false";
  257.         $offerList $query $this->getEntityManager()->createQuery($dql)->getResult();
  258.         $offerListByCategory = [];
  259.         $tagList = [];
  260.         $offerListCached = [];
  261.         $cityList = [];
  262.         $mediaRepository $this->getEntityManager()->getRepository(Media::class);
  263.         foreach ($offerList as $offer) {
  264.             $this->loadOfferData($offer$mediaRepository$tagList$offerListByCategory);
  265.             $offerListCached[$offer->getID()] = $offer;
  266.         }
  267.         $categoryListCached = [];
  268.         $categoryList $this->getEntityManager()->getRepository(Category::class)->getActiveCategoriesNotCached(Category::OFFER_CATEGORY_ID);
  269.         foreach ($categoryList as $category) {
  270.             if (!isset($offerListByCategory[$category->getID()])) {
  271.                 continue;
  272.             }
  273.             $offerList $offerListByCategory[$category->getID()];
  274.             if (count($offerList) == 0) {
  275.                 continue;
  276.             }
  277.             $category->getParentCategories()->toArray();
  278.             $category->setEntityCount(count($offerList));
  279.             $category->setHotFeedIconMedia($mediaRepository->getСategoryHotFeedIconMedia($category->getID()));
  280.             $category->getCity()->getID();
  281.             $city $category->getCity();
  282.             if (!isset($cityList[$city->getID()])) {
  283.                 $cityList[$city->getID()] = $city;
  284.             }
  285.             $categoryListCached[$category->getID()] = ['category' => $category'entityList' => $offerList];
  286.         }
  287.         $this->getEntityManager()->clear();
  288.         $softCache = new SoftCache(self::CACHE_NAME);
  289.         $softCache->setMulti($offerListCached0);
  290.         $softCache = new SoftCache(CategoryRepository::CACHE_NAME);
  291.         $softCache->set(CategoryRepository::ALL_CATEGORIES_CACHE_KEY$categoryListCached0);
  292.         $softCache->setMulti($categoryListCached0);
  293.         $softCache = new SoftCache(CityRepository::CACHE_NAME);
  294.         $softCache->set(CityRepository::CACHE_KEY_ACTIVE$cityList0);
  295.     }
  296.     /** @deprecated  */
  297.     public function reloadCacheForOneOffer($offerID$source '') {
  298.         $offerOrderRepository $this->getEntityManager()->getRepository(OfferOrder::class);
  299.         $logger Logger::instance('reloadCacheForOneOffer');
  300.         try {
  301.             $logger->info('Lock code pool for offer ' $offerID);
  302.             $offerOrderRepository->lockCodePool($offerID);
  303.             /** @var \Slivki\Entity\Offer $offer */
  304.             $offer $this->find($offerID);
  305.             if (!$offer->isActive() || !$offer->isInActivePeriod() || $offer->isHidden()) {
  306.                 $this->removeOfferFromCache($offerID);
  307.                 $logger->info('Unlock code pool for offer ' $offerID '. Offer is not active');
  308.                 $offerOrderRepository->unlockCodePool($offerID);
  309.                 return;
  310.             }
  311.             if ($offer->getDescriptionByType(EntityDescription::TYPE_OFFER_CONDITIONS_ID) == '') {
  312.                 Logger::instance('DESCRIPTION-DEBUG')->info("$offerID empty description. $source");
  313.             }
  314.             $mediaRepository $this->getEntityManager()->getRepository(Media::class);
  315.             $tagList = [];
  316.             $offerListByCategory = [];
  317.             $this->loadOfferData($offer$mediaRepository$tagList$offerListByCategory);
  318.             foreach ($offer->getGeoLocations() as $geoLocation) {
  319.                 $geoLocation->getPhoneNumbers()->toArray();
  320.             }
  321.             $softCache = new SoftCache(self::CACHE_NAME);
  322.             $softCache->set($offerID$offer0);
  323.             $logger->info('Unlock code pool for offer ' $offerID);
  324.             $offerOrderRepository->unlockCodePool($offerID);
  325.             $categoryList $this->getEntityManager()->getRepository(Category::class)->getAllActiveCategoriesFromCache();
  326.             $categoryListChanged false;
  327.             $categoryRepository $this->getEntityManager()->getRepository(Category::class);
  328.             $cityList $this->getEntityManager()->getRepository(City::class)->getActiveCitiesCached();
  329.             foreach ($offer->getCategories() as $category) {
  330.                 if (!isset($categoryList[$category->getID()])) {
  331.                     $category->getBanners()->toArray();
  332.                     $category->setEntityCount(1);
  333.                     $categoryForCache['category'] = $category;
  334.                     $categoryForCache['entityList'][] = $offerID;
  335.                     $categoryList[$category->getID()] = $categoryForCache;
  336.                     $categoryListChanged true;
  337.                     //$categoryRepository->putCategoryToCache($categoryForCache);
  338.                 }
  339.                 $city $category->getCity();
  340.                 if ($city && !isset($cityList[$city->getID()])) {
  341.                     $cityList[$city->getID()] = $city;
  342.                 }
  343.             }
  344.             foreach ($offerListByCategory as $categoryID => $category) {
  345.                 if (!isset($categoryList[$categoryID]['entityList'])) {
  346.                     $categoryList[$categoryID]['entityList'] = [];
  347.                 }
  348.                 if (!in_array($offerID$categoryList[$categoryID]['entityList'])) {
  349.                     $categoryList[$categoryID]['entityList'][] = $offerID;
  350.                     $categoryList[$categoryID]['category']->setEntityCount(count($categoryList[$categoryID]['entityList']));
  351.                     $categoryListChanged true;
  352.                     //$categoryRepository->putCategoryToCache($categoryList[$categoryID]);
  353.                 }
  354.             }
  355.             foreach ($categoryList as $categoryID => $category) {
  356.                 if (!isset($category['entityList']) || !$category['entityList']) {
  357.                     continue;
  358.                 }
  359.                 $index array_search($offerID$category['entityList']);
  360.                 if (false !== $index && !isset($offerListByCategory[$categoryID])) {
  361.                     unset($categoryList[$categoryID]['entityList'][$index]);
  362.                     $categoryList[$categoryID]['category']->setEntityCount(count($categoryList[$categoryID]['entityList']));
  363.                     $categoryListChanged true;
  364.                     //$categoryRepository->putCategoryToCache($categoryList[$categoryID]);
  365.                 }
  366.             }
  367.             if ($categoryListChanged) {
  368.                 $categoryRepository->cacheAllActiveCategories($categoryList);
  369.             }
  370.             $softCache = new SoftCache(CityRepository::CACHE_NAME);
  371.             $softCache->set(CityRepository::CACHE_KEY_ACTIVE$cityList0);
  372.         } catch (Exception $e) {
  373.             $logger->info('Unlock code pool for offer ' $offerID '. ' $e->getMessage());
  374.             $offerOrderRepository->unlockCodePool($offerID);
  375.         }
  376.         return $offer;
  377.     }
  378.     private function loadOfferData(Offer &$offerMediaRepository &$mediaRepository, &$tagList, &$offerListByCategory) {
  379.         $offerID $offer->getID();
  380.         if ($offer->getTeaserMedia()) {
  381.             $offer->getTeaserMedia()->getID();
  382.         }
  383.         $offer->setHotFeedIconMedia($mediaRepository->getOfferHotFeedIconMedia($offerID));
  384.         $offer->setDetailMeidas($mediaRepository->getOfferDetailMedias($offerID));
  385.         $offer->setShopMedias($mediaRepository->getOfferShopMedias($offerID));
  386.         $offer->getPhoneNumbers()->toArray();
  387.         $offer->getGiftCertificates()->toArray();
  388.         $offer->getExtensions()->toArray();
  389.         $offer->getOfferCodePools()->toArray();
  390.         $offer->getGeoLocations()->toArray();
  391.         $offer->getPhoneNumbers()->toArray();
  392.         $offer->getExtensions()->toArray();
  393.         $offer->getGiftCertificates()->toArray();
  394.         $offer->getSupplierPhotoMedias()->toArray();
  395.         foreach ($offer->getCategories() as $category) {
  396.             $offerListByCategory[$category->getID()][] = $offer->getID();
  397.         }
  398.         if($offer->isActiveCurrencyCalculator()) {
  399.             $offer->getBankCurrency()->getRate();
  400.         }
  401.         foreach ($offer->getDescriptions()->toArray() as $description) {
  402.             $description->getEntityDescriptionSliderImages()->toArray();
  403.         }
  404.     }
  405.     public function removeOfferFromCache($offerID) {
  406.         $softCache = new SoftCache(self::CACHE_NAME);
  407.         $offer $softCache->get($offerID);
  408.         if (!$offer) {
  409.             return;
  410.         }
  411.         $softCache->delete($offerID);
  412.         $categoryRepository $this->getEntityManager()->getRepository(Category::class);
  413.         $offersByCategory $categoryRepository->getAllActiveCategoriesFromCache();
  414.         $arrayChanged false;
  415.         foreach ($offersByCategory as $key => $category) {
  416.             if ($category['category']->getDomainObjectID() == Category::OFFER_CATEGORY_ID) {
  417.                 if (!isset($category['entityList']) || !is_array($category['entityList'])) {
  418.                     continue;
  419.                 }
  420.                 $entityCount count($category['entityList']);
  421.                 $offersByCategory[$key]['entityList'] = array_diff($category['entityList'], [$offerID]);
  422.                 if (count($offersByCategory[$key]['entityList']) != $entityCount) {
  423.                     $arrayChanged true;
  424.                     if (count($offersByCategory[$key]['entityList']) == 0) {
  425.                         unset($offersByCategory[$key]);
  426.                     }
  427.                 }
  428.             }
  429.         }
  430.         if ($arrayChanged) {
  431.             $categoryRepository->cacheAllActiveCategories($offersByCategory);
  432.         }
  433.     }
  434.     public function getOffersOnlyPayedMonthlyPurchaseCount($offerID) {
  435.         if (!self::$offersOnlyPayedMonthlyPurchaseCount) {
  436.             $dbConnection $this->getEntityManager()->getConnection();
  437.             $payedStatuses = [
  438.                 OfferOrder::STATUS_CONFIRM,
  439.                 OfferOrder::STATUS_EXTENSION_ORDER_CONFIRM,
  440.                 OfferOrder::STATUS_HALF_CONFIRM,
  441.                 OfferOrder::STATUS_BALANCE_CONFIRM
  442.             ];
  443.             $sql "select offer_order.offer_id, count(*) as amount from offer_code inner join offer_order on offer_code.offer_order_id = offer_order.id 
  444.                 where offer_order.status in (" join(','$payedStatuses). ") and offer_code.created_on > date_trunc('day', now() + '-30 days')::timestamp without time zone group by 1";
  445.             $offersOnlyPayedMonthlyPurchaseCount $dbConnection->executeQuery($sql)->fetchAll();
  446.             foreach ($offersOnlyPayedMonthlyPurchaseCount as $item) {
  447.                 self::$offersOnlyPayedMonthlyPurchaseCount[$item['offer_id']] = $item['amount'];
  448.             }
  449.         }
  450.         return isset(self::$offersOnlyPayedMonthlyPurchaseCount[$offerID]) ? self::$offersOnlyPayedMonthlyPurchaseCount[$offerID] : 0;
  451.     }
  452.     private function getPurchaseByCategory($categoryID$dateFrom$dateTo) {
  453.         $sql "select count(*) as amount from offer_order_details inner join offer_code_pool on offer_order_details.offer_code_pool_id = offer_code_pool.id  
  454.           inner join category2entity on offer_code_pool.offer_id = category2entity.entity_id where category2entity.category_id = $categoryID   
  455.           and offer_order_details.created_on between '$dateFrom' and '$dateTo'";
  456.         $result $this->getEntityManager()->getConnection()->executeQuery($sql)->fetch(\PDO::FETCH_COLUMN);
  457.         return (int)$result;
  458.     }
  459.     private function compareBannersByPosition(Banner $bannerBanner $banner1) {
  460.         if ($banner->getPositionRow() < $banner1->getPositionRow()) {
  461.             return -1;
  462.         }
  463.         if ($banner->getPositionRow() == $banner1->getPositionRow()) {
  464.             return $banner->getPositionColumn() > $banner1->getPositionColumn() ? : -1;
  465.         }
  466.         return 1;
  467.     }
  468.     public function getSortedOffersByPosition($categoryID) {
  469.         $dql "select position.entityID from Slivki:OfferCategoryPosition as position where position.categoryID = :categoryID order by position.position asc";
  470.         $offersListSorted $this->getEntityManager()->createQuery($dql)->setParameter('categoryID'$categoryID)->getResult();
  471.         return $offersListSorted;
  472.     }
  473.     public function getTeaserBanners(Category $category$teaserInARowCount ) {
  474.         $category $this->getEntityManager()->merge($category);
  475.         $teaserBanners $category->getActiveBannersByType(Banner::TYPE_TEASER_BANNER);
  476.         $teaserVerticalBanners $category->getActiveBannersByType(Banner::TYPE_TEASER_VERTICAL_BANNER);
  477.         $teaserBanners array_merge($teaserBanners$teaserVerticalBanners);
  478.         $teaserBannersPosition = array();
  479.         $withVerticalBannersRowList = array();
  480.         foreach ($teaserBanners as $banner) {
  481.             $position = ($banner->getPositionRow() - 1) * $banner->getPositionColumn();
  482.             if($banner->isActive()) {
  483.                 $teaserBannersPosition[$position] = $banner;
  484.                 if($banner->getTypeID() == Banner::TYPE_TEASER_VERTICAL_BANNER) {
  485.                     $withVerticalBannersRowList[] = (int)ceil($position $teaserInARowCount);
  486.                 }
  487.             };
  488.         }
  489.         return array(
  490.             'teaserBanners' => $teaserBannersPosition,
  491.             'withVerticalBannersRowList' => $withVerticalBannersRowList
  492.         );
  493.     }
  494.     public function getOffersByCategoryWithPositions($categoryID$limit null) {
  495.         $dql "select offer, offerCategoryPosition.position from Slivki:Offer offer
  496.           left join Slivki:OfferCategoryPosition offerCategoryPosition with offerCategoryPosition.entityID = offer.ID and offerCategoryPosition.categoryID = :categoryID
  497.           inner join offer.categories category where category.ID = :categoryID
  498.           order by offerCategoryPosition.position";
  499.         $query $this->getEntityManager()->createQuery($dql);
  500.         $query->setParameter('categoryID'$categoryID);
  501.         if ($limit) {
  502.             $query->setMaxResults((int)$limit);
  503.         }
  504.         return $query->getResult();
  505.     }
  506.     public function getActiveOffersByCategoryWithoutTags($domainObjectID$cityID) {
  507.         $dql "select offer.* from offer 
  508.             inner join category2entity on offer.id = category2entity.entity_id 
  509.             inner join category on category2entity.category_id = category.id 
  510.             inner join entity_option on offer.id = entity_option.entity_id
  511.             where city_id = $cityID and category.domain_object_id = $domainObjectID and offer.active and entity_option.name = '".EntityOption::OPTION_OFFERS_WITHOUT_TAGS."'
  512.             and offer.active_till > now()
  513.             group by offer.id";
  514.         return $this->getEntityManager()->getConnection()->executeQuery($dql)->fetchAll(Query::HYDRATE_ARRAY);
  515.     }
  516.     public function getActiveOffersByCategoryWithoutTagsAllCity() {
  517.         $dql "select offer.* from offer 
  518.             inner join category2entity on offer.id = category2entity.entity_id 
  519.             inner join category on category2entity.category_id = category.id 
  520.             inner join entity_option on offer.id = entity_option.entity_id
  521.             where offer.active and entity_option.name = '".EntityOption::OPTION_OFFERS_WITHOUT_TAGS."'
  522.             and offer.active_till > now()
  523.             group by offer.id";
  524.         return $this->getEntityManager()->getConnection()->executeQuery($dql)->fetchAll(Query::HYDRATE_ARRAY);
  525.     }
  526.     public function getOffersOnReview($domainObjectID$cityID) {
  527.         $dql "select offer.* from offer 
  528.             inner join category2entity on offer.id = category2entity.entity_id 
  529.             inner join category on category2entity.category_id = category.id 
  530.             where city_id = $cityID and category.domain_object_id = $domainObjectID and offer.review 
  531.             and offer.on_review group by offer.id";
  532.         return $this->getEntityManager()->getConnection()->executeQuery($dql)->fetchAll(Query::HYDRATE_ARRAY);
  533.     }
  534.     public function getOffersByCategorySortedByPopularity(Category $category) {
  535.         $categoryID $category->getID();
  536.         $offerList $this->getActiveOffersByCategoryIDNoCache($categoryID);
  537.         if (!$offerList) {
  538.             return false;
  539.         }
  540.         if ($categoryID != Category::NEW_OFFER_CATEGORY_ID) {
  541.             $dql "select purchaseCount from Slivki:PurchaseCount purchaseCount index by purchaseCount.entityID
  542.           inner join Slivki:Offer offer with purchaseCount.entityID = offer.ID
  543.           inner join offer.categories category where category.ID = :categoryID";
  544.             $query $this->getEntityManager()->createQuery($dql);
  545.             $query->setParameter('categoryID'$categoryID);
  546.             $query->execute();
  547.             self::$purchaseCountListByCategory $query->getResult();
  548.             if (self::$purchaseCountListByCategory) {
  549.                 usort($offerList, ['\Slivki\Repository\OfferRepository''compareOffersByPurchaseCount']);
  550.                 self::$purchaseCountListByCategory null;
  551.             }
  552.         } else {
  553.             usort($offerList, ['\Slivki\Repository\OfferRepository''compareOffersByRenewedOn']);
  554.         }
  555.         $payedPositions $this->getEntityManager()->getRepository(OfferPayedCategory::class)->findBy(
  556.             ['categoryID' => $categoryID], ['position' => 'asc']);
  557.         if ($payedPositions) {
  558.             foreach ($payedPositions as $position) {
  559.                 foreach ($offerList as $key => $offer) {
  560.                     if ($offer->getID() == $position->getEntityID()) {
  561.                         unset($offerList[$key]);
  562.                         array_splice($offerList$position->getPosition() - 10, [$offer]);
  563.                         break;
  564.                     }
  565.                 }
  566.             }
  567.         }
  568.         return $offerList;
  569.     }
  570.     private static function compareOffersByID(Offer $offerOffer $offer1) {
  571.         return $offer->getID() > $offer1->getID() ? -1;
  572.     }
  573.     private static function compareOffersByRenewedOn(Offer $offerOffer $offer1) {
  574.         if ($offer->getRenewedOn() == $offer1->getRenewedOn()) {
  575.             return $offer->getID() > $offer1->getID() ? : -1;
  576.         }
  577.         return $offer->getRenewedOn() > $offer1->getRenewedOn() ? -1;
  578.     }
  579.     private static function compareOffersByPurchaseCount(Offer $offerOffer $offer1) {
  580.         /** @var \Slivki\Entity\PurchaseCount $purchaseCount */
  581.         /** @var \Slivki\Entity\PurchaseCount $purchaseCount1 */
  582.         $purchaseCount = isset(self::$purchaseCountListByCategory[$offer->getID()]) ? self::$purchaseCountListByCategory[$offer->getID()] : new PurchaseCount();
  583.         $purchaseCount1 = isset(self::$purchaseCountListByCategory[$offer1->getID()]) ? self::$purchaseCountListByCategory[$offer1->getID()] : new PurchaseCount();
  584.         $purchaseCountKoeff $offer->getPurchaseKoeff() ?: ($offer->isInFreeCodesCategory() ? 1);
  585.         $purchaseCountKoeff1 $offer1->getPurchaseKoeff() ?: ($offer->isInFreeCodesCategory() ? 1);
  586.         if ($purchaseCount->getPurchaseCountRecent()/$purchaseCountKoeff == $purchaseCount1->getPurchaseCountRecent()/$purchaseCountKoeff1) {
  587.             if ($purchaseCount->getPurchaseCountLastMonth()/$purchaseCountKoeff == $purchaseCount1->getPurchaseCountLastMonth()/$purchaseCountKoeff1) {
  588.                 if ($purchaseCount->getPurchaseCount()/$purchaseCountKoeff == $purchaseCount1->getPurchaseCount()/$purchaseCountKoeff1) {
  589.                     return $offer->getID() > $offer1->getID() ? : -1;
  590.                 }
  591.                 return $purchaseCount->getPurchaseCount()/$purchaseCountKoeff $purchaseCount1->getPurchaseCount()/$purchaseCountKoeff1 ? -1;
  592.             } else {
  593.                 return $purchaseCount->getPurchaseCountLastMonth()/$purchaseCountKoeff $purchaseCount1->getPurchaseCountLastMonth()/$purchaseCountKoeff1 ? -1;
  594.             }
  595.         } else {
  596.             return $purchaseCount->getPurchaseCountRecent()/$purchaseCountKoeff $purchaseCount1->getPurchaseCountRecent()/$purchaseCountKoeff1 ? -1;
  597.         }
  598.     }
  599.     public function findPastOfferCached($offerID) {
  600.         $softCache = new SoftCache(self::CACHE_NAME);
  601.         $cacheKey 'pastOffer-0-' $offerID;
  602.         $offer $softCache->get($cacheKey);
  603.         if ($offer) {
  604.             return $offer;
  605.         }
  606.         $offer $this->find($offerID);
  607.         if (!$offer) {
  608.             return null;
  609.         }
  610.         $offer->getOfferCodePools()->toArray();
  611.         $offer->getGeoLocations()->toArray();
  612.         $softCache->set($cacheKey$offer0);
  613.         return $offer;
  614.     }
  615.     public function getVisitCount($offer$today false) {
  616.         if ($today) {
  617.             $dateFrom = new \DateTime('-1 days');
  618.         } else {
  619.             $dateFrom = new \DateTime('-30 days');
  620.         }
  621.         $dql "select count(visit.ID) from Slivki:Visit visit 
  622.           where visit.entityID = :offerID and visit.entityTypeID = :entityTypeID and visit.createdOn > :dateFrom and visit.createdOn > :offerActiveSince";
  623.         return $this->getEntityManager()->createQuery($dql)
  624.             ->setParameter('dateFrom'$dateFrom)
  625.             ->setParameter('offerID'$offer->getID())
  626.             ->setParameter('offerActiveSince'$offer->getActiveSince())
  627.             ->setParameter('entityTypeID'Category::OFFER_CATEGORY_ID)
  628.             ->getSingleScalarResult();
  629.     }
  630.     public function getLastPurchaseTime($offerID) {
  631.         $sql "select offer_order_details.created_on from offer_order_details inner join offer_order on offer_order_details.offer_order_id = offer_order.id
  632.           where offer_order.offer_id = $offerID and status > 0 and offer_order_details.created_on > now() + '-12 hours' order by offer_order_details.id desc limit 1";
  633.         $lastPurchaseTime $this->getEntityManager()->getConnection()->executeQuery($sql)->fetchColumn();
  634.         if ($lastPurchaseTime) {
  635.             $lastPurchaseTime = \DateTime::createFromFormat('Y-m-d H:i:s'$lastPurchaseTime);
  636.         }
  637.         return $lastPurchaseTime;
  638.     }
  639.     public function getRecentPurchaseCount($offerID) {
  640.         $purchaseData $this->getEntityManager()->getRepository(PurchaseCount::class)->findOneByEntityID($offerID);
  641.         if (!$purchaseData) {
  642.             return 0;
  643.         }
  644.         return $purchaseData->getPurchaseCountLastDay();
  645.     }
  646.     public function getTeaserWatermark($offerID) {
  647.         $offer $this->find($offerID);
  648.         if (!$offer) {
  649.             return [];
  650.         }
  651.         $mediaRepository $this->getEntityManager()->getRepository(Media::class);
  652.         $watermark $mediaRepository->getOfferTeaserLogo($offerID);
  653.         if ($watermark) {
  654.             return [
  655.                 'watermark' => $watermark,
  656.                 'width' => $offer->getTeaserLogoWidth(),
  657.                 'height' => $offer->getTeaserLogoHeight()
  658.             ];
  659.         }
  660.         $directors $offer->getDirectors();
  661.         /** @var Director $director */
  662.         foreach ($directors as $director) {
  663.             $watermark $mediaRepository->getDirectorLogo($director->getID());
  664.             if ($watermark) {
  665.                 return [
  666.                     'watermark' => $watermark,
  667.                     'width' => $director->getTeaserLogoWidth(),
  668.                     'height' => $director->getTeaserLogoHeight()
  669.                 ];
  670.             }
  671.         }
  672.         return [];
  673.     }
  674.     public function scheduleOfferReload($offerID) {
  675.         $scheduler = new CacheReloadScheduler();
  676.         $scheduler->setType(CacheReloadScheduler::TYPE_OFFER);
  677.         $scheduler->setEntityID($offerID);
  678.         $entityManager $this->getEntityManager();
  679.         $entityManager->persist($scheduler);
  680.         $entityManager->flush($scheduler);
  681.     }
  682.     public function getActiveOffersCount($cityID City::DEFAULT_CITY_ID) {
  683.         $sql "select count(distinct offer.id) from offer inner join category2entity on offer.id = category2entity.entity_id
  684.           inner join category on category2entity.category_id = category.id 
  685.           where offer.active and category.city_id = " . (int)$cityID " and now() between active_since and active_till";
  686.         return $this->getEntityManager()->getConnection()->executeQuery($sql)->fetchColumn(0);
  687.     }
  688.     public function getActiveOffersCountCached($cityID City::DEFAULT_CITY_ID) {
  689.         $softCache = new SoftCache(self::CACHE_NAME);
  690.         $cacheKey 'activeCount-' $cityID;
  691.         $offersCount $softCache->get($cacheKey);
  692.         if (!$offersCount) {
  693.             $offersCount $this->getActiveOffersCount($cityID);
  694.             $softCache->set($cacheKey$offersCount30 60);
  695.         }
  696.         return $offersCount;
  697.     }
  698.     public function getOfferGeoLocations(Offer $offer) {
  699.         $seoRepository $this->getEntityManager()->getRepository(Seo::class);
  700.         $result = [];
  701.         /** @var GeoLocation $geoLocation */
  702.         foreach($offer->getGeoLocations() as $geoLocation) {
  703.             $geoLocationStreet $geoLocation->getStreet() ? $geoLocation->getStreet() . ', ' '';
  704.             $geoLocationHouse $geoLocation->getHouse() ? $geoLocation->getHouse() : '';
  705.             $geoLocationDescription $geoLocation->getDescription() ? $geoLocation->getDescription() . ' - ' '';
  706.             $geoLocationInfos['markerAnnotation'] = $geoLocationDescription $geoLocationStreet $geoLocationHouse;
  707.             $geoLocationInfos['latitude'] = $geoLocation->getLatitude();
  708.             $geoLocationInfos['longitude'] = $geoLocation->getLongitude();
  709.             $geoLocationInfos['offerURI'] = $seoRepository->getOfferURL($offer->getID())->getMainAlias();
  710.             $result[]['geoLocationInfos'][] = $geoLocationInfos;
  711.         }
  712.         return $result;
  713.     }
  714.     public function getOfferGeoLocationData(Offer $offer$userGeoLocationImageService $imageService$jsonEncode true$baseURL '') {
  715.         $seoRepository $this->getEntityManager()->getRepository(Seo::class);
  716.         $features = [];
  717.         $url '';
  718.         $seo $seoRepository->getByEntity(SeoRepository::RESOURCE_URL_OFFER_DETAILS$offer->getID());
  719.         if ($seo) {
  720.             $url $seo->getMainAlias();
  721.         }
  722.         $closestLocationID 0;
  723.         $offerGeoLocations $offer->getGeoLocations();
  724.         foreach ($offerGeoLocations as $geoLocation) {
  725.             $caption $geoLocation->getStreet() . ', ' $geoLocation->getHouse();
  726.             $teaserURL '';
  727.             if ($offer->getTeaserMedia()) {
  728.                 $teaserURL $baseURL $imageService->getImageURL($offer->getTeaserMedia(), 00);
  729.             }
  730.             $features[] = [
  731.                 'type' => 'Feature',
  732.                 'id' => $geoLocation->getID(),
  733.                 'geometry' => [
  734.                     'type' => 'Point',
  735.                     'coordinates' => [$geoLocation->getLatitude(), $geoLocation->getLongitude()]
  736.                 ],
  737.                 'properties' => [
  738.                     'iconClass' => 'always-caption',
  739.                     'iconContent' => '',
  740.                     'locationID' => $geoLocation->getID(),
  741.                     'offerID' => $offer->getID(),
  742.                     'offerTitle' => $offer->getTitle(),
  743.                     'teaserURL' => $teaserURL,
  744.                     'offerType' => $offer->getOfferType(),
  745.                     'url' => $url,
  746.                     'hintContent' => $caption
  747.                 ]
  748.             ];
  749.         }
  750.         $getLocationData = ['type' => 'FeatureCollection''features' => $features];
  751.         if ($jsonEncode) {
  752.             $data json_encode($getLocationData);
  753.         } else {
  754.             $data $getLocationData;
  755.         }
  756.         return $data;
  757.     }
  758.     public function getOfferIDListByCategory($categoryID) {
  759.         $sql "select category2entity.entity_id from category2entity inner join offer on category2entity.entity_id = offer.id 
  760.               where category2entity.category_id = " . (int)$categoryID;
  761.         return $this->getEntityManager()->getConnection()->executeQuery($sql)->fetchAll(\PDO::FETCH_COLUMN);
  762.     }
  763.     public function getLastPurchaseDate($entityID$userID) {
  764.         $sql "select offer_order_details.created_on::date from offer_order_details inner join offer_order on offer_order_details.offer_order_id = offer_order.id
  765.             where offer_order.offer_id = $entityID and offer_order.user_id = $userID order by offer_order_details.id desc limit 1";
  766.         $lastPurchaseDate $this->getEntityManager()->getConnection()->executeQuery($sql)->fetchColumn();
  767.         return \DateTime::createFromFormat('Y-m-d'$lastPurchaseDate);
  768.     }
  769.     public function getOfferCityIDList(Offer $offer) {
  770.         $softCache = new SoftCache(self::OFFER_CITIES_CACHE_NAME);
  771.         $cityIDList $softCache->get($offer->getID());
  772.         if (!$cityIDList) {
  773.             $cityIDList $this->reloadOfferCityIDListCache($offer->getID());
  774.         }
  775.         return $cityIDList;
  776.     }
  777.     public function reloadOfferCityIDListCache($offerID) {
  778.         /** @var Offer $offer */
  779.         $offer $this->find($offerID);
  780.         if (!$offer) {
  781.             return false;
  782.         }
  783.         $cityIDList = [];
  784.         /** @var Category $category */
  785.         foreach ($offer->getCategories() as $category) {
  786.             $categoryID $category->getID();
  787.             if ($categoryID == Category::COUNTRY_CATEGORY_ID || $category->isChildOfRecursive(Category::COUNTRY_CATEGORY_ID)
  788.                 || $categoryID == Category::NEW_OFFER_CATEGORY_ID || $category->isChildOfRecursive(Category::NEW_OFFER_CATEGORY_ID)) {
  789.                 continue;
  790.             }
  791.             if ($category->getCity() && !in_array($category->getCity()->getID(), $cityIDList)) {
  792.                 $cityIDList[] = $category->getCity()->getID();
  793.             }
  794.         }
  795.         if (empty($cityIDList)) {
  796.             $cityIDList = [City::DEFAULT_CITY_ID];
  797.         }
  798.         $softCache = new SoftCache(self::OFFER_CITIES_CACHE_NAME);
  799.         $softCache->set($offerID$cityIDList0);
  800.         return $cityIDList;
  801.     }
  802.     public function getOfferCity($offerID): ?City
  803.     {
  804.         $offer $this->find($offerID);
  805.         /** @var Offer $offer */
  806.         $categories $offer->getCategories();
  807.         if (null !== $offer->getDefaultCategoryID()) {
  808.             $defaultCategoryId = (int) $offer->getDefaultCategoryID();
  809.             foreach ($categories as $category) {
  810.                 /** @var Category $category */
  811.                 if ($category->getID() === $defaultCategoryId) {
  812.                     return $category->getCity();
  813.                 }
  814.             }
  815.         }
  816.         return $offer->getCityByFirstCategory();
  817.     }
  818.     public function isOfferOwner(Offer $offerUser $user) {
  819.         if (!$user->hasRole(UserGroup::ROLE_SUPPLIER_ID)) {
  820.             return false;
  821.         }
  822.         foreach ($offer->getDirectors() as $director) {
  823.             if ($user->getEmail() == $director->getEmail()) {
  824.                 return true;
  825.             }
  826.         }
  827.         return false;
  828.     }
  829.     public function getExtensions(Offer $offer) {
  830.         $dql 'select extension from Slivki:FoodOfferExtension extension index by extension.partnerItemID where extension.offer = :offer and extension.active = true';
  831.         $query $this->getEntityManager()->createQuery($dql);
  832.         $query->setParameter('offer'$offer);
  833.         return $query->getResult();
  834.     }
  835.     public function getExtensionsByShippingType(Offer $offerstring $shippingType): array
  836.     {
  837.         $dql '
  838.                 SELECT 
  839.                     e 
  840.                 FROM Slivki:FoodOfferExtension AS e INDEX BY e.partnerItemID 
  841.                 WHERE e.offer = :offer 
  842.                     AND e.active = true
  843.                     AND e.is' ucfirst($shippingType) . ' = true
  844.                 ';
  845.         $query $this->getEntityManager()->createQuery($dql);
  846.         $query->setParameter('offer'$offer);
  847.         return $query->getResult();
  848.     }
  849.     public function getExtensionVariants(OfferExtension $offerExtension) {
  850.         $dql 'select extensionVariant from Slivki:OfferExtensionVariant extensionVariant index by extensionVariant.partnerID where extensionVariant.offerExtension = :offerExtension';
  851.         $query $this->getEntityManager()->createQuery($dql);
  852.         $query->setParameter('offerExtension'$offerExtension);
  853.         return $query->getResult();
  854.     }
  855.     public function getDirector($offerID) {
  856.         $dql 'select director from Slivki:Director director join director.offers offer where offer.ID = :offerID';
  857.         $query $this->getEntityManager()->createQuery($dql);
  858.         $query->setParameter('offerID'$offerID);
  859.         $result $query->execute();
  860.         if ($result) {
  861.             return $result[0];
  862.         }
  863.         return false;
  864.     }
  865.     public function getOfferConversion($offerID) {
  866.         $entityManager $this->getEntityManager();
  867.         $visitRepository $entityManager->getRepository(Visit::class);
  868.         $purchaseCountRepository $entityManager->getRepository(PurchaseCount::class);
  869.         $visitCount $visitRepository->getVisitCount($offerIDVisit::TYPE_OFFER30);
  870.         $purchaseCount $purchaseCountRepository->findOneBy(['entityID' => $offerID]);
  871.         if (!$visitCount || !$purchaseCount || !$purchaseCount->getPurchaseCountLastMonth()) {
  872.             return 0;
  873.         }
  874.         return ceil(100 * ($purchaseCount->getPurchaseCountLastMonth() / $visitCount));
  875.     }
  876.     public function getCityID(int $offerID) : int {
  877.         return $this->getEntityManager()->getConnection()->executeQuery(
  878.             "select coalesce(max(city_id), " City::DEFAULT_CITY_ID ") from category"
  879.             " inner join category2entity on category.id = category2entity.category_id"
  880.             " where category2entity.entity_id = " $offerID
  881.         )->fetchColumn();
  882.     }
  883.     private function getHasPurchaseOffersByCityIdQuery(
  884.         int $cityId,
  885.         Period $period
  886.     ) : QueryBuilder {
  887.         $qb $this->createQueryBuilder('hasPurchaseOffer');
  888.         $expr $qb->expr();
  889.         return  $qb->innerJoin('hasPurchaseOffer.offerOrders''offerOrder')
  890.             ->innerJoin('hasPurchaseOffer.categories''hasPurchaseOfferCategories')
  891.             ->andWhere($expr->eq('hasPurchaseOfferCategories.city'':cityId'))
  892.             ->andWhere($expr->gt('offerOrder.status'':status'))
  893.             ->andWhere($expr->between('offerOrder.createdOn'':dateFrom'':dateTo'))
  894.             ->distinct()
  895.             ->setParameters([
  896.                 'cityId' => $cityId,
  897.                 'status' => OfferOrder::STATUS_INIT,
  898.                 'dateFrom' => $period->getStartDate(),
  899.                 'dateTo' => $period->getEndDate(),
  900.             ]);
  901.     }
  902.     /**
  903.      * @return Offer[]
  904.      */
  905.     public function getHasNotPurchaseOffersByCityId(
  906.         int $cityId,
  907.         Period $period
  908.     ) : iterable {
  909.         $qb $this->createQueryBuilder('offer');
  910.         $expr $qb->expr();
  911.         $getHasPurchaseOffersByCityIdDql $this->getHasPurchaseOffersByCityIdQuery($cityId$period)->getDQL();
  912.         return  $qb->innerJoin('offer.categories''categories')
  913.             ->andWhere($expr->eq('categories.city'':cityId'))
  914.             ->andWhere($expr->eq('offer.active'':active'))
  915.             ->andWhere($expr->between(':now''offer.activeSince''offer.activeTill'))
  916.             ->andWhere($expr->lt('offer.activeSince'':dateTo'))
  917.             ->andWhere(sprintf('offer NOT IN (%s)'$getHasPurchaseOffersByCityIdDql))
  918.             ->setParameters([
  919.                 'cityId' => $cityId,
  920.                 'status' => OfferOrder::STATUS_INIT,
  921.                 'active' => true,
  922.                 'dateFrom' => $period->getStartDate(),
  923.                 'dateTo' => $period->getEndDate(),
  924.                 'now' => (new \DateTimeImmutable())->format('Y-m-d H:i:s'),
  925.             ])
  926.             ->getQuery()
  927.             ->getResult();
  928.     }
  929.     /**
  930.      * @return Offer[]
  931.      */
  932.     public function getOffersOnlineOrdersStat(int $onlineOrderAllowedRemovedAtDaysBefore): array
  933.     {
  934.         $qb $this->createQueryBuilder('offer');
  935.         $expr $qb->expr();
  936.         return $qb
  937.             ->andWhere(
  938.                 $expr->orX(
  939.                     $expr->isNotNull('offer.allowedOnlineOrderTypes'),
  940.                     $expr->gt('offer.onlineOrderAllowedRemovedAt'':onlineOrderAllowedRemovedAt'),
  941.                 )
  942.             )
  943.             ->setParameters([
  944.                 'onlineOrderAllowedRemovedAt' => (new \DateTimeImmutable())
  945.                     ->modify(\sprintf('-%d days'$onlineOrderAllowedRemovedAtDaysBefore))
  946.                     ->format('Y-m-d H:i:s'),
  947.             ])
  948.             ->getQuery()
  949.             ->getResult();
  950.     }
  951.     /**
  952.      * @return Offer[]
  953.      */
  954.     public function getActiveOffersByCityId(int $cityId): array
  955.     {
  956.         $queryBuilder $this->createQueryBuilder('offer');
  957.         $expr $queryBuilder->expr();
  958.         return $queryBuilder
  959.             ->innerJoin('offer.categories''category')
  960.             ->innerJoin('category.city''city')
  961.             ->andWhere($expr->eq('city.ID'':cityId'))
  962.             ->andWhere($expr->eq('offer.active'':active'))
  963.             ->andWhere($expr->neq('offer.hidden'':hidden'))
  964.             ->andWhere($expr->between(':now''offer.activeSince''offer.activeTill'))
  965.             ->setParameters([
  966.                 'cityId' => $cityId,
  967.                 'active' => true,
  968.                 'hidden' => true,
  969.                 'now' => (new \DateTimeImmutable())->format('Y-m-d H:i:s'),
  970.             ])
  971.             ->getQuery()
  972.             ->getResult();
  973.     }
  974. }