src/Repository/OfferRepository.php line 199

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.         if ($offer && $offer->getID() == 283793) {
  216.             return 0;
  217.         }
  218.         $siteSettingsRepository $this->getEntityManager()->getRepository(SiteSettings::class);
  219.         if (!$offer) {
  220.             return $siteSettingsRepository->getSiteSettingsCached()->getCodeCost();
  221.         }
  222.         if ($offer->getCodeCostByCount()) {
  223.             $codeCost null;
  224.             foreach ($offer->getCodeCostByCount() as $count => $cost) {
  225.                 if ($count $codesCount) {
  226.                     break;
  227.                 }
  228.                 $codeCost $cost;
  229.             }
  230.             return $codeCost;
  231.         }
  232.         if ($offer->getCodeCost() !== null) {
  233.             return $offer->getCodeCost();
  234.         }
  235.         return $siteSettingsRepository->getSiteSettingsCached()->getCodeCost();
  236.     }
  237.     public function getOfferExtensionCodeCost(OfferExtension $extension) {
  238.         if ($extension->getCodePrice() != null) {
  239.             return $extension->getCodePrice();
  240.         }
  241.         return $this->getCodeCost($extension->getOffer());
  242.     }
  243.     /** @deprecated  */
  244.     public function putOfferToCache(Offer $offer) {
  245.         $softCache = new SoftCache(self::CACHE_NAME);
  246.         $cacheKey $offer->getID() . self::LOCKED_KEY;
  247.         while (!$softCache->add($cacheKeyself::LOCKED_KEY60));
  248.         $softCache->set($offer->getID(), $offer0);
  249.         $softCache->set($cacheKeynull1);
  250.     }
  251.     public function reloadCache() {
  252.         $dql "select offer, descriptions, offerCodePools, geoLocations, category from Slivki:Offer offer index by offer.ID inner join offer.categories category
  253.           left join offer.descriptions as descriptions left join offer.offerCodePools offerCodePools left join offer.geoLocations as geoLocations  
  254.           where category.active = true and offer.active = true and CURRENT_TIMESTAMP() between offer.activeSince and offer.activeTill
  255.           and offer.hidden = false";
  256.         $offerList $query $this->getEntityManager()->createQuery($dql)->getResult();
  257.         $offerListByCategory = [];
  258.         $tagList = [];
  259.         $offerListCached = [];
  260.         $cityList = [];
  261.         $mediaRepository $this->getEntityManager()->getRepository(Media::class);
  262.         foreach ($offerList as $offer) {
  263.             $this->loadOfferData($offer$mediaRepository$tagList$offerListByCategory);
  264.             $offerListCached[$offer->getID()] = $offer;
  265.         }
  266.         $categoryListCached = [];
  267.         $categoryList $this->getEntityManager()->getRepository(Category::class)->getActiveCategoriesNotCached(Category::OFFER_CATEGORY_ID);
  268.         foreach ($categoryList as $category) {
  269.             if (!isset($offerListByCategory[$category->getID()])) {
  270.                 continue;
  271.             }
  272.             $offerList $offerListByCategory[$category->getID()];
  273.             if (count($offerList) == 0) {
  274.                 continue;
  275.             }
  276.             $category->getParentCategories()->toArray();
  277.             $category->setEntityCount(count($offerList));
  278.             $category->setHotFeedIconMedia($mediaRepository->getСategoryHotFeedIconMedia($category->getID()));
  279.             $category->getCity()->getID();
  280.             $city $category->getCity();
  281.             if (!isset($cityList[$city->getID()])) {
  282.                 $cityList[$city->getID()] = $city;
  283.             }
  284.             $categoryListCached[$category->getID()] = ['category' => $category'entityList' => $offerList];
  285.         }
  286.         $this->getEntityManager()->clear();
  287.         $softCache = new SoftCache(self::CACHE_NAME);
  288.         $softCache->setMulti($offerListCached0);
  289.         $softCache = new SoftCache(CategoryRepository::CACHE_NAME);
  290.         $softCache->set(CategoryRepository::ALL_CATEGORIES_CACHE_KEY$categoryListCached0);
  291.         $softCache->setMulti($categoryListCached0);
  292.         $softCache = new SoftCache(CityRepository::CACHE_NAME);
  293.         $softCache->set(CityRepository::CACHE_KEY_ACTIVE$cityList0);
  294.     }
  295.     /** @deprecated  */
  296.     public function reloadCacheForOneOffer($offerID$source '') {
  297.         $offerOrderRepository $this->getEntityManager()->getRepository(OfferOrder::class);
  298.         $logger Logger::instance('reloadCacheForOneOffer');
  299.         try {
  300.             $logger->info('Lock code pool for offer ' $offerID);
  301.             $offerOrderRepository->lockCodePool($offerID);
  302.             /** @var \Slivki\Entity\Offer $offer */
  303.             $offer $this->find($offerID);
  304.             if (!$offer->isActive() || !$offer->isInActivePeriod() || $offer->isHidden()) {
  305.                 $this->removeOfferFromCache($offerID);
  306.                 $logger->info('Unlock code pool for offer ' $offerID '. Offer is not active');
  307.                 $offerOrderRepository->unlockCodePool($offerID);
  308.                 return;
  309.             }
  310.             if ($offer->getDescriptionByType(EntityDescription::TYPE_OFFER_CONDITIONS_ID) == '') {
  311.                 Logger::instance('DESCRIPTION-DEBUG')->info("$offerID empty description. $source");
  312.             }
  313.             $mediaRepository $this->getEntityManager()->getRepository(Media::class);
  314.             $tagList = [];
  315.             $offerListByCategory = [];
  316.             $this->loadOfferData($offer$mediaRepository$tagList$offerListByCategory);
  317.             foreach ($offer->getGeoLocations() as $geoLocation) {
  318.                 $geoLocation->getPhoneNumbers()->toArray();
  319.             }
  320.             $softCache = new SoftCache(self::CACHE_NAME);
  321.             $softCache->set($offerID$offer0);
  322.             $logger->info('Lock code pool for offer ' $offerID);
  323.             $offerOrderRepository->unlockCodePool($offerID);
  324.             $categoryList $this->getEntityManager()->getRepository(Category::class)->getAllActiveCategoriesFromCache();
  325.             $categoryListChanged false;
  326.             $categoryRepository $this->getEntityManager()->getRepository(Category::class);
  327.             $cityList $this->getEntityManager()->getRepository(City::class)->getActiveCitiesCached();
  328.             foreach ($offer->getCategories() as $category) {
  329.                 if (!isset($categoryList[$category->getID()])) {
  330.                     $category->getBanners()->toArray();
  331.                     $category->setEntityCount(1);
  332.                     $categoryForCache['category'] = $category;
  333.                     $categoryForCache['entityList'][] = $offerID;
  334.                     $categoryList[$category->getID()] = $categoryForCache;
  335.                     $categoryListChanged true;
  336.                     $categoryRepository->putCategoryToCache($categoryForCache);
  337.                 }
  338.                 $city $category->getCity();
  339.                 if ($city && !isset($cityList[$city->getID()])) {
  340.                     $cityList[$city->getID()] = $city;
  341.                 }
  342.             }
  343.             foreach ($offerListByCategory as $categoryID => $category) {
  344.                 if (!isset($categoryList[$categoryID]['entityList'])) {
  345.                     $categoryList[$categoryID]['entityList'] = [];
  346.                 }
  347.                 if (!in_array($offerID$categoryList[$categoryID]['entityList'])) {
  348.                     $categoryList[$categoryID]['entityList'][] = $offerID;
  349.                     $categoryList[$categoryID]['category']->setEntityCount(count($categoryList[$categoryID]['entityList']));
  350.                     $categoryListChanged true;
  351.                     $categoryRepository->putCategoryToCache($categoryList[$categoryID]);
  352.                 }
  353.             }
  354.             foreach ($categoryList as $categoryID => $category) {
  355.                 if (!isset($category['entityList']) || !$category['entityList']) {
  356.                     continue;
  357.                 }
  358.                 $index array_search($offerID$category['entityList']);
  359.                 if (false !== $index && !isset($offerListByCategory[$categoryID])) {
  360.                     unset($categoryList[$categoryID]['entityList'][$index]);
  361.                     $categoryList[$categoryID]['category']->setEntityCount(count($categoryList[$categoryID]['entityList']));
  362.                     $categoryListChanged true;
  363.                     $categoryRepository->putCategoryToCache($categoryList[$categoryID]);
  364.                 }
  365.             }
  366.             if ($categoryListChanged) {
  367.                 $categoryRepository->cacheAllActiveCategories($categoryList);
  368.             }
  369.             $softCache = new SoftCache(CityRepository::CACHE_NAME);
  370.             $softCache->set(CityRepository::CACHE_KEY_ACTIVE$cityList0);
  371.         } catch (Exception $e) {
  372.             $logger->info('Unlock code pool for offer ' $offerID '. ' $e->getMessage());
  373.             $offerOrderRepository->unlockCodePool($offerID);
  374.         }
  375.         return $offer;
  376.     }
  377.     private function loadOfferData(Offer &$offerMediaRepository &$mediaRepository, &$tagList, &$offerListByCategory) {
  378.         $offerID $offer->getID();
  379.         if ($offer->getTeaserMedia()) {
  380.             $offer->getTeaserMedia()->getID();
  381.         }
  382.         $offer->setHotFeedIconMedia($mediaRepository->getOfferHotFeedIconMedia($offerID));
  383.         $offer->setDetailMeidas($mediaRepository->getOfferDetailMedias($offerID));
  384.         $offer->setShopMedias($mediaRepository->getOfferShopMedias($offerID));
  385.         $offer->getPhoneNumbers()->toArray();
  386.         $offer->getGiftCertificates()->toArray();
  387.         $offer->getExtensions()->toArray();
  388.         $offer->getOfferCodePools()->toArray();
  389.         $offer->getGeoLocations()->toArray();
  390.         $offer->getPhoneNumbers()->toArray();
  391.         $offer->getExtensions()->toArray();
  392.         $offer->getGiftCertificates()->toArray();
  393.         $offer->getSupplierPhotoMedias()->toArray();
  394.         foreach ($offer->getCategories() as $category) {
  395.             $offerListByCategory[$category->getID()][] = $offer->getID();
  396.         }
  397.         if($offer->isActiveCurrencyCalculator()) {
  398.             $offer->getBankCurrency()->getRate();
  399.         }
  400.         foreach ($offer->getDescriptions()->toArray() as $description) {
  401.             $description->getEntityDescriptionSliderImages()->toArray();
  402.         }
  403.     }
  404.     public function removeOfferFromCache($offerID) {
  405.         $softCache = new SoftCache(self::CACHE_NAME);
  406.         $offer $softCache->get($offerID);
  407.         if (!$offer) {
  408.             return;
  409.         }
  410.         $softCache->delete($offerID);
  411.         $categoryRepository $this->getEntityManager()->getRepository(Category::class);
  412.         $offersByCategory $categoryRepository->getAllActiveCategoriesFromCache();
  413.         $arrayChanged false;
  414.         foreach ($offersByCategory as $key => $category) {
  415.             if ($category['category']->getDomainObjectID() == Category::OFFER_CATEGORY_ID) {
  416.                 if (!isset($category['entityList']) || !is_array($category['entityList'])) {
  417.                     continue;
  418.                 }
  419.                 $entityCount count($category['entityList']);
  420.                 $offersByCategory[$key]['entityList'] = array_diff($category['entityList'], [$offerID]);
  421.                 if (count($offersByCategory[$key]['entityList']) != $entityCount) {
  422.                     $arrayChanged true;
  423.                     if (count($offersByCategory[$key]['entityList']) == 0) {
  424.                         unset($offersByCategory[$key]);
  425.                     }
  426.                 }
  427.             }
  428.         }
  429.         if ($arrayChanged) {
  430.             $categoryRepository->cacheAllActiveCategories($offersByCategory);
  431.         }
  432.     }
  433.     public function getOffersOnlyPayedMonthlyPurchaseCount($offerID) {
  434.         if (!self::$offersOnlyPayedMonthlyPurchaseCount) {
  435.             $dbConnection $this->getEntityManager()->getConnection();
  436.             $payedStatuses = [
  437.                 OfferOrder::STATUS_CONFIRM,
  438.                 OfferOrder::STATUS_EXTENSION_ORDER_CONFIRM,
  439.                 OfferOrder::STATUS_HALF_CONFIRM,
  440.                 OfferOrder::STATUS_BALANCE_CONFIRM
  441.             ];
  442.             $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 
  443.                 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";
  444.             $offersOnlyPayedMonthlyPurchaseCount $dbConnection->executeQuery($sql)->fetchAll();
  445.             foreach ($offersOnlyPayedMonthlyPurchaseCount as $item) {
  446.                 self::$offersOnlyPayedMonthlyPurchaseCount[$item['offer_id']] = $item['amount'];
  447.             }
  448.         }
  449.         return isset(self::$offersOnlyPayedMonthlyPurchaseCount[$offerID]) ? self::$offersOnlyPayedMonthlyPurchaseCount[$offerID] : 0;
  450.     }
  451.     private function getPurchaseByCategory($categoryID$dateFrom$dateTo) {
  452.         $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  
  453.           inner join category2entity on offer_code_pool.offer_id = category2entity.entity_id where category2entity.category_id = $categoryID   
  454.           and offer_order_details.created_on between '$dateFrom' and '$dateTo'";
  455.         $result $this->getEntityManager()->getConnection()->executeQuery($sql)->fetch(\PDO::FETCH_COLUMN);
  456.         return (int)$result;
  457.     }
  458.     private function compareBannersByPosition(Banner $bannerBanner $banner1) {
  459.         if ($banner->getPositionRow() < $banner1->getPositionRow()) {
  460.             return -1;
  461.         }
  462.         if ($banner->getPositionRow() == $banner1->getPositionRow()) {
  463.             return $banner->getPositionColumn() > $banner1->getPositionColumn() ? : -1;
  464.         }
  465.         return 1;
  466.     }
  467.     public function getSortedOffersByPosition($categoryID) {
  468.         $dql "select position.entityID from Slivki:OfferCategoryPosition as position where position.categoryID = :categoryID order by position.position asc";
  469.         $offersListSorted $this->getEntityManager()->createQuery($dql)->setParameter('categoryID'$categoryID)->getResult();
  470.         return $offersListSorted;
  471.     }
  472.     public function getTeaserBanners(Category $category$teaserInARowCount ) {
  473.         $category $this->getEntityManager()->merge($category);
  474.         $teaserBanners $category->getActiveBannersByType(Banner::TYPE_TEASER_BANNER);
  475.         $teaserVerticalBanners $category->getActiveBannersByType(Banner::TYPE_TEASER_VERTICAL_BANNER);
  476.         $teaserBanners array_merge($teaserBanners$teaserVerticalBanners);
  477.         $teaserBannersPosition = array();
  478.         $withVerticalBannersRowList = array();
  479.         foreach ($teaserBanners as $banner) {
  480.             $position = ($banner->getPositionRow() - 1) * $banner->getPositionColumn();
  481.             if($banner->isActive()) {
  482.                 $teaserBannersPosition[$position] = $banner;
  483.                 if($banner->getTypeID() == Banner::TYPE_TEASER_VERTICAL_BANNER) {
  484.                     $withVerticalBannersRowList[] = (int)ceil($position $teaserInARowCount);
  485.                 }
  486.             };
  487.         }
  488.         return array(
  489.             'teaserBanners' => $teaserBannersPosition,
  490.             'withVerticalBannersRowList' => $withVerticalBannersRowList
  491.         );
  492.     }
  493.     public function getOffersByCategoryWithPositions($categoryID$limit null) {
  494.         $dql "select offer, offerCategoryPosition.position from Slivki:Offer offer
  495.           left join Slivki:OfferCategoryPosition offerCategoryPosition with offerCategoryPosition.entityID = offer.ID and offerCategoryPosition.categoryID = :categoryID
  496.           inner join offer.categories category where category.ID = :categoryID
  497.           order by offerCategoryPosition.position";
  498.         $query $this->getEntityManager()->createQuery($dql);
  499.         $query->setParameter('categoryID'$categoryID);
  500.         if ($limit) {
  501.             $query->setMaxResults((int)$limit);
  502.         }
  503.         return $query->getResult();
  504.     }
  505.     public function getActiveOffersByCategoryWithoutTags($domainObjectID$cityID) {
  506.         $dql "select offer.* from offer 
  507.             inner join category2entity on offer.id = category2entity.entity_id 
  508.             inner join category on category2entity.category_id = category.id 
  509.             inner join entity_option on offer.id = entity_option.entity_id
  510.             where city_id = $cityID and category.domain_object_id = $domainObjectID and offer.active and entity_option.name = '".EntityOption::OPTION_OFFERS_WITHOUT_TAGS."'
  511.             and offer.active_till > now()
  512.             group by offer.id";
  513.         return $this->getEntityManager()->getConnection()->executeQuery($dql)->fetchAll(Query::HYDRATE_ARRAY);
  514.     }
  515.     public function getActiveOffersByCategoryWithoutTagsAllCity() {
  516.         $dql "select offer.* from offer 
  517.             inner join category2entity on offer.id = category2entity.entity_id 
  518.             inner join category on category2entity.category_id = category.id 
  519.             inner join entity_option on offer.id = entity_option.entity_id
  520.             where offer.active and entity_option.name = '".EntityOption::OPTION_OFFERS_WITHOUT_TAGS."'
  521.             and offer.active_till > now()
  522.             group by offer.id";
  523.         return $this->getEntityManager()->getConnection()->executeQuery($dql)->fetchAll(Query::HYDRATE_ARRAY);
  524.     }
  525.     public function getOffersOnReview($domainObjectID$cityID) {
  526.         $dql "select offer.* from offer 
  527.             inner join category2entity on offer.id = category2entity.entity_id 
  528.             inner join category on category2entity.category_id = category.id 
  529.             where city_id = $cityID and category.domain_object_id = $domainObjectID and offer.review 
  530.             and offer.on_review group by offer.id";
  531.         return $this->getEntityManager()->getConnection()->executeQuery($dql)->fetchAll(Query::HYDRATE_ARRAY);
  532.     }
  533.     public function getOffersByCategorySortedByPopularity(Category $category) {
  534.         $categoryID $category->getID();
  535.         $offerList $this->getActiveOffersByCategoryIDNoCache($categoryID);
  536.         if (!$offerList) {
  537.             return false;
  538.         }
  539.         if ($categoryID != Category::NEW_OFFER_CATEGORY_ID) {
  540.             $dql "select purchaseCount from Slivki:PurchaseCount purchaseCount index by purchaseCount.entityID
  541.           inner join Slivki:Offer offer with purchaseCount.entityID = offer.ID
  542.           inner join offer.categories category where category.ID = :categoryID";
  543.             $query $this->getEntityManager()->createQuery($dql);
  544.             $query->setParameter('categoryID'$categoryID);
  545.             $query->execute();
  546.             self::$purchaseCountListByCategory $query->getResult();
  547.             if (self::$purchaseCountListByCategory) {
  548.                 usort($offerList, ['\Slivki\Repository\OfferRepository''compareOffersByPurchaseCount']);
  549.                 self::$purchaseCountListByCategory null;
  550.             }
  551.         } else {
  552.             usort($offerList, ['\Slivki\Repository\OfferRepository''compareOffersByRenewedOn']);
  553.         }
  554.         $payedPositions $this->getEntityManager()->getRepository(OfferPayedCategory::class)->findBy(
  555.             ['categoryID' => $categoryID], ['position' => 'asc']);
  556.         if ($payedPositions) {
  557.             foreach ($payedPositions as $position) {
  558.                 foreach ($offerList as $key => $offer) {
  559.                     if ($offer->getID() == $position->getEntityID()) {
  560.                         unset($offerList[$key]);
  561.                         array_splice($offerList$position->getPosition() - 10, [$offer]);
  562.                         break;
  563.                     }
  564.                 }
  565.             }
  566.         }
  567.         return $offerList;
  568.     }
  569.     private static function compareOffersByID(Offer $offerOffer $offer1) {
  570.         return $offer->getID() > $offer1->getID() ? -1;
  571.     }
  572.     private static function compareOffersByRenewedOn(Offer $offerOffer $offer1) {
  573.         if ($offer->getRenewedOn() == $offer1->getRenewedOn()) {
  574.             return $offer->getID() > $offer1->getID() ? : -1;
  575.         }
  576.         return $offer->getRenewedOn() > $offer1->getRenewedOn() ? -1;
  577.     }
  578.     private static function compareOffersByPurchaseCount(Offer $offerOffer $offer1) {
  579.         /** @var \Slivki\Entity\PurchaseCount $purchaseCount */
  580.         /** @var \Slivki\Entity\PurchaseCount $purchaseCount1 */
  581.         $purchaseCount = isset(self::$purchaseCountListByCategory[$offer->getID()]) ? self::$purchaseCountListByCategory[$offer->getID()] : new PurchaseCount();
  582.         $purchaseCount1 = isset(self::$purchaseCountListByCategory[$offer1->getID()]) ? self::$purchaseCountListByCategory[$offer1->getID()] : new PurchaseCount();
  583.         $purchaseCountKoeff $offer->getPurchaseKoeff() ?: ($offer->isInFreeCodesCategory() ? 1);
  584.         $purchaseCountKoeff1 $offer1->getPurchaseKoeff() ?: ($offer->isInFreeCodesCategory() ? 1);
  585.         if ($purchaseCount->getPurchaseCountRecent()/$purchaseCountKoeff == $purchaseCount1->getPurchaseCountRecent()/$purchaseCountKoeff1) {
  586.             if ($purchaseCount->getPurchaseCountLastMonth()/$purchaseCountKoeff == $purchaseCount1->getPurchaseCountLastMonth()/$purchaseCountKoeff1) {
  587.                 if ($purchaseCount->getPurchaseCount()/$purchaseCountKoeff == $purchaseCount1->getPurchaseCount()/$purchaseCountKoeff1) {
  588.                     return $offer->getID() > $offer1->getID() ? : -1;
  589.                 }
  590.                 return $purchaseCount->getPurchaseCount()/$purchaseCountKoeff $purchaseCount1->getPurchaseCount()/$purchaseCountKoeff1 ? -1;
  591.             } else {
  592.                 return $purchaseCount->getPurchaseCountLastMonth()/$purchaseCountKoeff $purchaseCount1->getPurchaseCountLastMonth()/$purchaseCountKoeff1 ? -1;
  593.             }
  594.         } else {
  595.             return $purchaseCount->getPurchaseCountRecent()/$purchaseCountKoeff $purchaseCount1->getPurchaseCountRecent()/$purchaseCountKoeff1 ? -1;
  596.         }
  597.     }
  598.     public function findPastOfferCached($offerID) {
  599.         $softCache = new SoftCache(self::CACHE_NAME);
  600.         $cacheKey 'pastOffer-0-' $offerID;
  601.         $offer $softCache->get($cacheKey);
  602.         if ($offer) {
  603.             return $offer;
  604.         }
  605.         $offer $this->find($offerID);
  606.         if (!$offer) {
  607.             return null;
  608.         }
  609.         $offer->getOfferCodePools()->toArray();
  610.         $offer->getGeoLocations()->toArray();
  611.         $softCache->set($cacheKey$offer0);
  612.         return $offer;
  613.     }
  614.     public function getVisitCount($offer$today false) {
  615.         if ($today) {
  616.             $dateFrom = new \DateTime('-1 days');
  617.         } else {
  618.             $dateFrom = new \DateTime('-30 days');
  619.         }
  620.         $dql "select count(visit.ID) from Slivki:Visit visit 
  621.           where visit.entityID = :offerID and visit.entityTypeID = :entityTypeID and visit.createdOn > :dateFrom and visit.createdOn > :offerActiveSince";
  622.         return $this->getEntityManager()->createQuery($dql)
  623.             ->setParameter('dateFrom'$dateFrom)
  624.             ->setParameter('offerID'$offer->getID())
  625.             ->setParameter('offerActiveSince'$offer->getActiveSince())
  626.             ->setParameter('entityTypeID'Category::OFFER_CATEGORY_ID)
  627.             ->getSingleScalarResult();
  628.     }
  629.     public function getLastPurchaseTime($offerID) {
  630.         $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
  631.           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";
  632.         $lastPurchaseTime $this->getEntityManager()->getConnection()->executeQuery($sql)->fetchColumn();
  633.         if ($lastPurchaseTime) {
  634.             $lastPurchaseTime = \DateTime::createFromFormat('Y-m-d H:i:s'$lastPurchaseTime);
  635.         }
  636.         return $lastPurchaseTime;
  637.     }
  638.     public function getRecentPurchaseCount($offerID) {
  639.         $purchaseData $this->getEntityManager()->getRepository(PurchaseCount::class)->findOneByEntityID($offerID);
  640.         if (!$purchaseData) {
  641.             return 0;
  642.         }
  643.         return $purchaseData->getPurchaseCountLastDay();
  644.     }
  645.     public function getTeaserWatermark($offerID) {
  646.         $offer $this->find($offerID);
  647.         if (!$offer) {
  648.             return [];
  649.         }
  650.         $mediaRepository $this->getEntityManager()->getRepository(Media::class);
  651.         $watermark $mediaRepository->getOfferTeaserLogo($offerID);
  652.         if ($watermark) {
  653.             return [
  654.                 'watermark' => $watermark,
  655.                 'width' => $offer->getTeaserLogoWidth(),
  656.                 'height' => $offer->getTeaserLogoHeight()
  657.             ];
  658.         }
  659.         $directors $offer->getDirectors();
  660.         /** @var Director $director */
  661.         foreach ($directors as $director) {
  662.             $watermark $mediaRepository->getDirectorLogo($director->getID());
  663.             if ($watermark) {
  664.                 return [
  665.                     'watermark' => $watermark,
  666.                     'width' => $director->getTeaserLogoWidth(),
  667.                     'height' => $director->getTeaserLogoHeight()
  668.                 ];
  669.             }
  670.         }
  671.         return [];
  672.     }
  673.     public function scheduleOfferReload($offerID) {
  674.         $scheduler = new CacheReloadScheduler();
  675.         $scheduler->setType(CacheReloadScheduler::TYPE_OFFER);
  676.         $scheduler->setEntityID($offerID);
  677.         $entityManager $this->getEntityManager();
  678.         $entityManager->persist($scheduler);
  679.         $entityManager->flush($scheduler);
  680.     }
  681.     public function getActiveOffersCount($cityID City::DEFAULT_CITY_ID) {
  682.         $sql "select count(distinct offer.id) from offer inner join category2entity on offer.id = category2entity.entity_id
  683.           inner join category on category2entity.category_id = category.id 
  684.           where offer.active and category.city_id = " . (int)$cityID " and now() between active_since and active_till";
  685.         return $this->getEntityManager()->getConnection()->executeQuery($sql)->fetchColumn(0);
  686.     }
  687.     public function getActiveOffersCountCached($cityID City::DEFAULT_CITY_ID) {
  688.         $softCache = new SoftCache(self::CACHE_NAME);
  689.         $cacheKey 'activeCount-' $cityID;
  690.         $offersCount $softCache->get($cacheKey);
  691.         if (!$offersCount) {
  692.             $offersCount $this->getActiveOffersCount($cityID);
  693.             $softCache->set($cacheKey$offersCount30 60);
  694.         }
  695.         return $offersCount;
  696.     }
  697.     public function getOfferGeoLocations(Offer $offer) {
  698.         $seoRepository $this->getEntityManager()->getRepository(Seo::class);
  699.         $result = [];
  700.         /** @var GeoLocation $geoLocation */
  701.         foreach($offer->getGeoLocations() as $geoLocation) {
  702.             $geoLocationStreet $geoLocation->getStreet() ? $geoLocation->getStreet() . ', ' '';
  703.             $geoLocationHouse $geoLocation->getHouse() ? $geoLocation->getHouse() : '';
  704.             $geoLocationDescription $geoLocation->getDescription() ? $geoLocation->getDescription() . ' - ' '';
  705.             $geoLocationInfos['markerAnnotation'] = $geoLocationDescription $geoLocationStreet $geoLocationHouse;
  706.             $geoLocationInfos['latitude'] = $geoLocation->getLatitude();
  707.             $geoLocationInfos['longitude'] = $geoLocation->getLongitude();
  708.             $geoLocationInfos['offerURI'] = $seoRepository->getOfferURL($offer->getID())->getMainAlias();
  709.             $result[]['geoLocationInfos'][] = $geoLocationInfos;
  710.         }
  711.         return $result;
  712.     }
  713.     public function getOfferGeoLocationData(Offer $offer$userGeoLocationImageService $imageService$jsonEncode true$baseURL '') {
  714.         $seoRepository $this->getEntityManager()->getRepository(Seo::class);
  715.         $features = [];
  716.         $url '';
  717.         $seo $seoRepository->getByEntity(SeoRepository::RESOURCE_URL_OFFER_DETAILS$offer->getID());
  718.         if ($seo) {
  719.             $url $seo->getMainAlias();
  720.         }
  721.         $closestLocationID 0;
  722.         $offerGeoLocations $offer->getGeoLocations();
  723.         foreach ($offerGeoLocations as $geoLocation) {
  724.             $caption $geoLocation->getStreet() . ', ' $geoLocation->getHouse();
  725.             $teaserURL '';
  726.             if ($offer->getTeaserMedia()) {
  727.                 $teaserURL $baseURL $imageService->getImageURL($offer->getTeaserMedia(), 00);
  728.             }
  729.             $features[] = [
  730.                 'type' => 'Feature',
  731.                 'id' => $geoLocation->getID(),
  732.                 'geometry' => [
  733.                     'type' => 'Point',
  734.                     'coordinates' => [$geoLocation->getLatitude(), $geoLocation->getLongitude()]
  735.                 ],
  736.                 'properties' => [
  737.                     'iconClass' => 'always-caption',
  738.                     'iconContent' => '',
  739.                     'locationID' => $geoLocation->getID(),
  740.                     'offerID' => $offer->getID(),
  741.                     'offerTitle' => $offer->getTitle(),
  742.                     'teaserURL' => $teaserURL,
  743.                     'offerType' => $offer->getOfferType(),
  744.                     'url' => $url,
  745.                     'hintContent' => $caption
  746.                 ]
  747.             ];
  748.         }
  749.         $getLocationData = ['type' => 'FeatureCollection''features' => $features];
  750.         if ($jsonEncode) {
  751.             $data json_encode($getLocationData);
  752.         } else {
  753.             $data $getLocationData;
  754.         }
  755.         return $data;
  756.     }
  757.     public function getOfferIDListByCategory($categoryID) {
  758.         $sql "select category2entity.entity_id from category2entity inner join offer on category2entity.entity_id = offer.id 
  759.               where category2entity.category_id = " . (int)$categoryID;
  760.         return $this->getEntityManager()->getConnection()->executeQuery($sql)->fetchAll(\PDO::FETCH_COLUMN);
  761.     }
  762.     public function getLastPurchaseDate($entityID$userID) {
  763.         $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
  764.             where offer_order.offer_id = $entityID and offer_order.user_id = $userID order by offer_order_details.id desc limit 1";
  765.         $lastPurchaseDate $this->getEntityManager()->getConnection()->executeQuery($sql)->fetchColumn();
  766.         return \DateTime::createFromFormat('Y-m-d'$lastPurchaseDate);
  767.     }
  768.     public function getOfferCityIDList(Offer $offer) {
  769.         $softCache = new SoftCache(self::OFFER_CITIES_CACHE_NAME);
  770.         $cityIDList $softCache->get($offer->getID());
  771.         if (!$cityIDList) {
  772.             $cityIDList $this->reloadOfferCityIDListCache($offer->getID());
  773.         }
  774.         return $cityIDList;
  775.     }
  776.     public function reloadOfferCityIDListCache($offerID) {
  777.         /** @var Offer $offer */
  778.         $offer $this->find($offerID);
  779.         if (!$offer) {
  780.             return false;
  781.         }
  782.         $cityIDList = [];
  783.         /** @var Category $category */
  784.         foreach ($offer->getCategories() as $category) {
  785.             $categoryID $category->getID();
  786.             if ($categoryID == Category::COUNTRY_CATEGORY_ID || $category->isChildOfRecursive(Category::COUNTRY_CATEGORY_ID)
  787.                 || $categoryID == Category::NEW_OFFER_CATEGORY_ID || $category->isChildOfRecursive(Category::NEW_OFFER_CATEGORY_ID)) {
  788.                 continue;
  789.             }
  790.             if ($category->getCity() && !in_array($category->getCity()->getID(), $cityIDList)) {
  791.                 $cityIDList[] = $category->getCity()->getID();
  792.             }
  793.         }
  794.         if (empty($cityIDList)) {
  795.             $cityIDList = [City::DEFAULT_CITY_ID];
  796.         }
  797.         $softCache = new SoftCache(self::OFFER_CITIES_CACHE_NAME);
  798.         $softCache->set($offerID$cityIDList0);
  799.         return $cityIDList;
  800.     }
  801.     public function getOfferCity($offerID): ?City
  802.     {
  803.         $offer $this->find($offerID);
  804.         /** @var Offer $offer */
  805.         $categories $offer->getCategories();
  806.         if (null !== $offer->getDefaultCategoryID()) {
  807.             $defaultCategoryId = (int) $offer->getDefaultCategoryID();
  808.             foreach ($categories as $category) {
  809.                 /** @var Category $category */
  810.                 if ($category->getID() === $defaultCategoryId) {
  811.                     return $category->getCity();
  812.                 }
  813.             }
  814.         }
  815.         return $offer->getCityByFirstCategory();
  816.     }
  817.     public function isOfferOwner(Offer $offerUser $user) {
  818.         if (!$user->hasRole(UserGroup::ROLE_SUPPLIER_ID)) {
  819.             return false;
  820.         }
  821.         foreach ($offer->getDirectors() as $director) {
  822.             if ($user->getEmail() == $director->getEmail()) {
  823.                 return true;
  824.             }
  825.         }
  826.         return false;
  827.     }
  828.     public function getExtensions(Offer $offer) {
  829.         $dql 'select extension from Slivki:FoodOfferExtension extension index by extension.partnerItemID where extension.offer = :offer and extension.active = true';
  830.         $query $this->getEntityManager()->createQuery($dql);
  831.         $query->setParameter('offer'$offer);
  832.         return $query->getResult();
  833.     }
  834.     public function getExtensionsByShippingType(Offer $offerstring $shippingType): array
  835.     {
  836.         $dql '
  837.                 SELECT 
  838.                     e 
  839.                 FROM Slivki:FoodOfferExtension AS e INDEX BY e.partnerItemID 
  840.                 WHERE e.offer = :offer 
  841.                     AND e.active = true
  842.                     AND e.is' ucfirst($shippingType) . ' = true
  843.                 ';
  844.         $query $this->getEntityManager()->createQuery($dql);
  845.         $query->setParameter('offer'$offer);
  846.         return $query->getResult();
  847.     }
  848.     public function getExtensionVariants(OfferExtension $offerExtension) {
  849.         $dql 'select extensionVariant from Slivki:OfferExtensionVariant extensionVariant index by extensionVariant.partnerID where extensionVariant.offerExtension = :offerExtension';
  850.         $query $this->getEntityManager()->createQuery($dql);
  851.         $query->setParameter('offerExtension'$offerExtension);
  852.         return $query->getResult();
  853.     }
  854.     public function getDirector($offerID) {
  855.         $dql 'select director from Slivki:Director director join director.offers offer where offer.ID = :offerID';
  856.         $query $this->getEntityManager()->createQuery($dql);
  857.         $query->setParameter('offerID'$offerID);
  858.         $result $query->execute();
  859.         if ($result) {
  860.             return $result[0];
  861.         }
  862.         return false;
  863.     }
  864.     public function getOfferConversion($offerID) {
  865.         $entityManager $this->getEntityManager();
  866.         $visitRepository $entityManager->getRepository(Visit::class);
  867.         $purchaseCountRepository $entityManager->getRepository(PurchaseCount::class);
  868.         $visitCount $visitRepository->getVisitCount($offerIDVisit::TYPE_OFFER30);
  869.         $purchaseCount $purchaseCountRepository->findOneBy(['entityID' => $offerID]);
  870.         if (!$visitCount || !$purchaseCount || !$purchaseCount->getPurchaseCountLastMonth()) {
  871.             return 0;
  872.         }
  873.         return ceil(100 * ($purchaseCount->getPurchaseCountLastMonth() / $visitCount));
  874.     }
  875.     public function getCityID(int $offerID) : int {
  876.         return $this->getEntityManager()->getConnection()->executeQuery(
  877.             "select coalesce(max(city_id), " City::DEFAULT_CITY_ID ") from category"
  878.             " inner join category2entity on category.id = category2entity.category_id"
  879.             " where category2entity.entity_id = " $offerID
  880.         )->fetchColumn();
  881.     }
  882.     private function getHasPurchaseOffersByCityIdQuery(
  883.         int $cityId,
  884.         Period $period
  885.     ) : QueryBuilder {
  886.         $qb $this->createQueryBuilder('hasPurchaseOffer');
  887.         $expr $qb->expr();
  888.         return  $qb->innerJoin('hasPurchaseOffer.offerOrders''offerOrder')
  889.             ->innerJoin('hasPurchaseOffer.categories''hasPurchaseOfferCategories')
  890.             ->andWhere($expr->eq('hasPurchaseOfferCategories.city'':cityId'))
  891.             ->andWhere($expr->gt('offerOrder.status'':status'))
  892.             ->andWhere($expr->between('offerOrder.createdOn'':dateFrom'':dateTo'))
  893.             ->distinct()
  894.             ->setParameters([
  895.                 'cityId' => $cityId,
  896.                 'status' => OfferOrder::STATUS_INIT,
  897.                 'dateFrom' => $period->getStartDate(),
  898.                 'dateTo' => $period->getEndDate(),
  899.             ]);
  900.     }
  901.     /**
  902.      * @return Offer[]
  903.      */
  904.     public function getHasNotPurchaseOffersByCityId(
  905.         int $cityId,
  906.         Period $period
  907.     ) : iterable {
  908.         $qb $this->createQueryBuilder('offer');
  909.         $expr $qb->expr();
  910.         $getHasPurchaseOffersByCityIdDql $this->getHasPurchaseOffersByCityIdQuery($cityId$period)->getDQL();
  911.         return  $qb->innerJoin('offer.categories''categories')
  912.             ->andWhere($expr->eq('categories.city'':cityId'))
  913.             ->andWhere($expr->eq('offer.active'':active'))
  914.             ->andWhere($expr->between(':now''offer.activeSince''offer.activeTill'))
  915.             ->andWhere($expr->lt('offer.activeSince'':dateTo'))
  916.             ->andWhere(sprintf('offer NOT IN (%s)'$getHasPurchaseOffersByCityIdDql))
  917.             ->setParameters([
  918.                 'cityId' => $cityId,
  919.                 'status' => OfferOrder::STATUS_INIT,
  920.                 'active' => true,
  921.                 'dateFrom' => $period->getStartDate(),
  922.                 'dateTo' => $period->getEndDate(),
  923.                 'now' => (new \DateTimeImmutable())->format('Y-m-d H:i:s'),
  924.             ])
  925.             ->getQuery()
  926.             ->getResult();
  927.     }
  928.     /**
  929.      * @return Offer[]
  930.      */
  931.     public function getOffersOnlineOrdersStat(int $onlineOrderAllowedRemovedAtDaysBefore): array
  932.     {
  933.         $qb $this->createQueryBuilder('offer');
  934.         $expr $qb->expr();
  935.         return $qb
  936.             ->andWhere(
  937.                 $expr->orX(
  938.                     $expr->isNotNull('offer.allowedOnlineOrderTypes'),
  939.                     $expr->gt('offer.onlineOrderAllowedRemovedAt'':onlineOrderAllowedRemovedAt'),
  940.                 )
  941.             )
  942.             ->setParameters([
  943.                 'onlineOrderAllowedRemovedAt' => (new \DateTimeImmutable())
  944.                     ->modify(\sprintf('-%d days'$onlineOrderAllowedRemovedAtDaysBefore))
  945.                     ->format('Y-m-d H:i:s'),
  946.             ])
  947.             ->getQuery()
  948.             ->getResult();
  949.     }
  950.     /**
  951.      * @return Offer[]
  952.      */
  953.     public function getActiveOffersByCityId(int $cityId): array
  954.     {
  955.         $queryBuilder $this->createQueryBuilder('offer');
  956.         $expr $queryBuilder->expr();
  957.         return $queryBuilder
  958.             ->innerJoin('offer.categories''category')
  959.             ->innerJoin('category.city''city')
  960.             ->andWhere($expr->eq('city.ID'':cityId'))
  961.             ->andWhere($expr->eq('offer.active'':active'))
  962.             ->andWhere($expr->neq('offer.hidden'':hidden'))
  963.             ->andWhere($expr->between(':now''offer.activeSince''offer.activeTill'))
  964.             ->setParameters([
  965.                 'cityId' => $cityId,
  966.                 'active' => true,
  967.                 'hidden' => true,
  968.                 'now' => (new \DateTimeImmutable())->format('Y-m-d H:i:s'),
  969.             ])
  970.             ->getQuery()
  971.             ->getResult();
  972.     }
  973. }