<?php
namespace Slivki\Services\Sale;
use Doctrine\ORM\EntityManagerInterface;
use Slivki\Entity\Category;
use Slivki\Entity\GeoLocation;
use Slivki\Entity\Media;
use Slivki\Entity\OfferPayedCategory;
use Slivki\Entity\Sale;
use Slivki\Repository\Category\CategoryRepositoryInterface;
use Slivki\Repository\SaleRepository;
use Slivki\Services\CacheService;
use Slivki\Services\TextCacheService;
use Slivki\Util\SoftCache;
use Slivki\Util\TarantoolCache;
use Symfony\Component\Security\Acl\Exception\Exception;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
use Symfony\Component\Serializer\Serializer;
class SaleCacheService extends CacheService {
public const SPACE_NAME = 'sale';
private EntityManagerInterface $entityManager;
private CategoryRepositoryInterface $categoryRepository;
public function __construct(
TextCacheService $textCacheService,
EntityManagerInterface $entityManager,
CategoryRepositoryInterface $categoryRepository
) {
parent::__construct($textCacheService);
$this->entityManager = $entityManager;
$this->categoryRepository = $categoryRepository;
}
public function reloadSaleCache($saleID, $reloadMemcache = true) {
$sale = null;
if ($reloadMemcache) {
$sale = $this->entityManager->getRepository(Sale::class)->reloadCacheForSale($saleID);
} else {
$sale = $this->entityManager->getRepository(Sale::class)->findCached($saleID);
}
if (!$sale) {
$this->deleteSale($saleID);
return false;
}
$tarantool = new TarantoolCache(self::SPACE_NAME);
$tarantool->set($saleID, [$saleID, json_encode($sale)]);
$this->cacheGeoLocations($saleID, $sale->getGeoLocations()->toArray());
return $sale;
}
public function getSale($saleID, $fullData = false) {
$saleID = (int)$saleID;
$tarantool = new TarantoolCache(self::SPACE_NAME);
$data = $tarantool->get($saleID);
if (!$data) {
return $this->entityManager->getRepository(Sale::class)->getActiveSaleByID($saleID);
}
$serializer = new Serializer([new ObjectNormalizer()], [new JsonEncoder()]);
/** @var Sale $sale */
$sale = $serializer->deserialize($data[0][1], Sale::class, 'json');
$sale->fromJSON(json_decode($data[0][1]));
$sale->setIcon($this->entityManager->getRepository(Media::class)->getSaleIconMedia($sale->getID()));
if ($fullData) {
$sale->setGeoLocations($this->getGeoLocations(GeoLocation::TYPE, GeoLocation::class, $saleID));
}
return $sale;
}
public function deleteSale($saleID) {
$tarantool = new TarantoolCache(self::SPACE_NAME);
$tarantool->callFunction('deleteSale', [(int)$saleID]);
}
public function getSalesByCategoryID($categoryID) {
$softCache = new SoftCache(SaleRepository::CACHE_NAME);
$saleListByCategory = $softCache->get(SaleRepository::ACTIVE_SALES_BY_CATEGORY);
$tarantool = new TarantoolCache();
$sales = [];
if (isset($saleListByCategory[$categoryID])) {
$salesData = $tarantool->callFunction('getSales', [$saleListByCategory[$categoryID]]);
if (!$salesData || (isset($salesData[0][0][0]) && count($salesData[0][0][0]) == 0)) {
return [];
}
foreach ($salesData[0][0] as $data) {
$sale = $this->unserializeSale($data);
if ($sale) {
$sales[] = $sale;
}
}
}
return $sales;
}
private function unserializeSale($data) {
if (count($data) == 0) {
return false;
}
$serializer = new Serializer([new ObjectNormalizer()], [new JsonEncoder()]);
/** @var Sale $sale */
$sale = $serializer->deserialize($data[0][1], Sale::class, 'json');
$sale->fromJSON(json_decode($data[0][1]));
$sale->setGeoLocations($this->getGeoLocations(GeoLocation::TYPE, GeoLocation::class, $data[0][0]));
$sale->setIcon($this->entityManager->getRepository(Media::class)->getSaleIconMedia($sale->getID()));
$sale->setFlierMedia($this->entityManager->getRepository(Media::class)->getFlierImageMedia($sale->getID()));
return $sale;
}
public function getSalesSorted($categoryID) {
$saleList = $this->getSalesByCategoryID($categoryID);
$payedPositions = $this->entityManager->getRepository(OfferPayedCategory::class)->findBy(
['categoryID' => $categoryID], ['position' => 'asc']);
if ($payedPositions) {
foreach ($payedPositions as $position) {
foreach ($saleList as $key => $sale) {
if ($sale->getID() == $position->getEntityID()) {
unset($saleList[$key]);
array_splice($saleList, $position->getPosition() - 1, 0, [$sale]);
break;
}
}
}
}
return $saleList;
}
public function getRelatedSales(Sale $sale, &$salesOffset) {
$result = [];
$this->getRelatedSalesByCategory($sale, SaleRepository::POPULAR_SALE_CATEGORY_ID, $result, false, $salesOffset, 'popular');
$this->getRelatedSalesByCategory($sale, Category::SALE_VIDEO_GUIDE_CATEGORY_ID, $result, false, $salesOffset, 'new');
$categoryID = null;
$entiityManager = $this->entityManager;
$sale = $entiityManager->merge($sale);
$entiityManager->detach($sale);
foreach ($sale->getCategories() as $category) {
if ($category->getEntityCount() > 3 && !in_array($category->getID(), [SaleRepository::POPULAR_SALE_CATEGORY_ID, SaleRepository::NEW_CATEGORY_ID, Category::PHOTOGUIDE_SALE_CATEGORY_ID])) {
$categoryID = $category->getID();
break;
}
}
if (!$categoryID) {
foreach ($sale->getCategories() as $category) {
if ($category->getID() == SaleRepository::POPULAR_SALE_CATEGORY_ID || $category->getEntityCount() < 4) {
continue;
}
$categoryID = $category->getID();
if (in_array($categoryID, [SaleRepository::SOS_CATEGORY_ID, SaleRepository::NEW_CATEGORY_ID, Category::PHOTOGUIDE_SALE_CATEGORY_ID])) {
$this->getRelatedSalesByCategory($sale, $categoryID, $result, true, $salesOffset, 'extra');
} else {
$this->getRelatedSalesByCategory($sale, $categoryID, $result, false, $salesOffset, 'extra');
}
break;
}
} else {
$this->getRelatedSalesByCategory($sale, $categoryID, $result, false, $salesOffset, 'extra');
}
return $result;
}
private function getRelatedSalesByCategory(Sale $sale, $categoryID, &$saleList, $isFakeCategory, &$salesOffset, $key) {
$count = 0;
$limit = 4;
$salesByCategory = $this->getSalesSorted($categoryID);
$countSalesByCategory = count($salesByCategory);
if (!isset($salesOffset[$key]) || $salesOffset[$key] * $limit >= ceil($countSalesByCategory / 2)) {
$salesOffset[$key] = 0;
}
$salesByCategory = array_slice($salesByCategory, $salesOffset[$key] * $limit);
$salesOffset[$key]++;
foreach ($salesByCategory as $saleItem) {
if ($sale->getID() != $saleItem->getID()) {
$found = false;
foreach ($saleList as $category) {
foreach ($category as $saleListItem) {
if ($saleListItem->getID() == $saleItem->getID()) {
$found = true;
break;
}
}
}
if (!$found) {
if($isFakeCategory) {
$saleList[SaleRepository::FAKE_CATEGORY_ID][] = $saleItem;
} else {
$saleList[$categoryID][] = $saleItem;
}
$count++;
}
if ($count == $limit) {
return;
}
}
}
}
// TODO: Refactor this piece of shit
public function getActiveSalesGroupedByCategory(): array
{
$saleCategoryList = $this->categoryRepository->findParentActiveSaleCategories();
$activeSalesAmount = 0;
$salesList = [];
foreach ($saleCategoryList as $saleCategory) {
$saleList = $this->getSalesByCategoryID($saleCategory->getID());
$activeSalesAmount += count($saleList);
$categoryID = $saleCategory->getID();
$payedPositions = $this->entityManager->getRepository(OfferPayedCategory::class)->findBy(
['categoryID' => $categoryID], ['position' => 'asc']);
if ($payedPositions) {
foreach ($payedPositions as $position) {
foreach ($saleList as $key => $sale) {
if ($sale->getID() == $position->getEntityID()) {
unset($saleList[$key]);
array_splice($saleList, $position->getPosition() - 1, 0, [$sale]);
break;
}
}
}
}
$categoryItem = [
"category" => $saleCategory,
"saleList" => $saleList
];
if ($saleCategory->getID() == Category::COMPANY_NEWS_SALE_CATEGORY_ID) {
array_unshift($salesList, $categoryItem);
} else {
$salesList[] = $categoryItem;
}
}
return ['salesList' => $salesList, 'activeSalesAmount' => $activeSalesAmount];
}
}