src/Repository/OfferRepository.php line 845

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