<?php
namespace Slivki\Services\Offer;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\EntityManagerInterface;
use Slivki\Entity\BankCurrency;
use Slivki\Entity\Category;
use Slivki\Entity\GeoLocation;
use Slivki\Entity\Media;
use Slivki\Entity\Media\OfferTeaserMedia;
use Slivki\Entity\MediaType;
use Slivki\Entity\Offer;
use Slivki\Entity\OfferExtension;
use Slivki\Entity\PhoneNumber;
use Slivki\Entity\Seo;
use Slivki\Repository\SeoRepository;
use Slivki\Services\CacheService;
use Slivki\Services\TextCacheService;
use Slivki\Util\Logger;
use Slivki\Util\TarantoolCache;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
use Symfony\Component\Serializer\Serializer;
class OfferCacheService extends CacheService {
const SPACE_NAME = 'offer';
const PHONE_SPACE_NAME = 'phoneNumber';
const EXTENSION_SPACE_NAME = 'offerExtension';
const PHONE_2_LOCATION_SPACE_NAME = 'phoneNumber2geoLocation';
private $entityManager;
public function __construct(EntityManagerInterface $entityManager, TextCacheService $textCacheService) {
$this->entityManager = $entityManager;
$this->textCacheService = $textCacheService;
}
public function reloadOfferCache($offerID, $reloadMemcache = true) {
/** @var Offer $offer */
$offer = null;
$offerID = (int)$offerID;
if ($reloadMemcache) {
$offer = $this->entityManager->getRepository(Offer::class)->reloadCacheForOneOffer($offerID, 'OfferCacheService::reloadOfferCache');
} else {
$offer = $this->entityManager->getRepository(Offer::class)->findCached($offerID);
}
$tarantool = new TarantoolCache(self::SPACE_NAME);
if (!$offer) {
$this->deleteOffer($offerID);
return false;
}
foreach ($offer->getCategories() as $category) {
if ($category->getTypeID() == Category::SUPPLIER_CATEGORY_TYPE) {
$categoryData = [
'name' => $category->getName(),
'url' => $this->entityManager->getRepository(Seo::class)->getByEntity(SeoRepository::RESOURCE_URL_OFFER_CATEGORY, $category->getID())->getMainAlias()
];
$offer->setSupplierCategoryData($categoryData);
}
}
$teaserMediaID = $offer->getTeaserMedia() ? $offer->getTeaserMedia()->getID() : null;
$mediaTarantool = new TarantoolCache(self::MEDIA_SPACE_NAME);
if ($teaserMediaID) {
$medias = $mediaTarantool->callFunction('getMedias', [OfferTeaserMedia::TYPE, 0, $offerID]);
if ($medias) {
foreach ($medias as $media) {
if ($media[0] != $teaserMediaID) {
$mediaTarantool->delete($media[0]);
}
}
}
}
$shopMedias = $this->getMediaList($offerID, 0, MediaType::TYPE_OFFER_SHOP_PHOTO_ID);
foreach ($shopMedias as $tarantoolMedia) {
$found = false;
foreach ($offer->getShopMedias() as $media) {
if ($tarantoolMedia->getID() == $media->getID()) {
$found = true;
break;
}
}
if (!$found) {
$tarantool->delete($tarantoolMedia->getID());
}
}
$tarantool->set($offerID, [$offerID, json_encode($offer)]);
$this->cacheMedias([$offer->getTeaserMedia()]);
$this->cacheMedias([$offer->getMobileTeaserMedia()]);
$this->cacheMedias($offer->getDetailMedias());
$this->cacheMedias($offer->getShopMedias());
$this->cacheMedias([$offer->getAppTeaserMedia()]);
$medias = $mediaTarantool->callFunction('getMedias', [Media\OfferTopBlockMedia::TYPE, 0, $offerID]);
$mediaID = $offer->getTopBlockMedia() ? $offer->getTopBlockMedia()->getID() : 0;
if ($medias) {
foreach ($medias as $media) {
if ($media[0] != $mediaID) {
$mediaTarantool->delete($media[0]);
}
}
}
$medias = $mediaTarantool->callFunction('getMedias', [Media\OfferTopBlockMobileMedia::TYPE, 0, $offerID]);
$mediaID = $offer->getTopBlockMobileMedia() ? $offer->getTopBlockMobileMedia()->getID() : 0;
if ($medias) {
foreach ($medias as $media) {
if ($media[0] != $mediaID) {
$mediaTarantool->delete($media[0]);
}
}
}
$this->cacheMedias([$offer->getTopBlockMedia()]);
$this->cacheMedias([$offer->getTopBlockMobileMedia()]);
$medias = $mediaTarantool->callFunction('getMedias', [Media\OfferMapLogoMedia::TYPE, 0, $offerID]);
$mapLogoMedia = $offer->getMapLogoMedia();
$mediaId = null === $mapLogoMedia ? 0 : $mapLogoMedia->getID();
foreach ($medias as $media) {
if ($media[0] !== $mediaId) {
$mediaTarantool->delete($media[0]);
}
}
$this->cacheMedias($offer->getMapLogoMedias()->toArray());
$medias = $mediaTarantool->callFunction('getMedias', [Media\OfferDeliveryZoneMedia::TYPE, 0, $offerID]);
$deliveryZoneMedia = $offer->getDeliveryZoneMedia();
$mediaId = null === $deliveryZoneMedia ? 0 : $deliveryZoneMedia->getID();
foreach ($medias as $media) {
if ($media[0] !== $mediaId) {
$mediaTarantool->delete($media[0]);
}
}
$this->cacheMedias($offer->getDeliveryZoneMedias()->toArray());
$medias = $mediaTarantool->callFunction('getMedias', [Media\OnlineOrderPopupLogoMedia::TYPE, 0, $offerID]);
$onlineOrderPopupLogoMedia = $offer->getOnlineOrderPopupLogoMedia();
$mediaId = null === $onlineOrderPopupLogoMedia ? 0 : $onlineOrderPopupLogoMedia->getID();
foreach ($medias as $media) {
if ($media[0] !== $mediaId) {
$mediaTarantool->delete($media[0]);
}
}
$this->cacheMedias($offer->getOnlineOrderPopupLogoMedias()->toArray());
if ($offer->getSupplierPhotoMedias()) {
$this->cacheMedias($offer->getSupplierPhotoMedias()->toArray());
}
$this->cacheEntityDescriptions($offer->getDescriptions()->toArray());
if ($offer->getGeoLocations()) {
$this->cacheGeoLocations($offerID, $offer->getGeoLocations()->toArray());
}
$this->cachePhoneNumbers($offerID, $offer->getPhoneNumbers()->toArray());
$this->cacheExtensions($offerID, $offer->getExtensions()->toArray());
return $offer;
}
public function cacheOffer(Offer $offer) {
$offerID = $offer->getID();
$this->entityManager->getRepository(Offer::class)->putOfferToCache($offer);
$tarantool = new TarantoolCache(self::SPACE_NAME);
$tarantool->set($offerID, [$offerID, json_encode($offer)]);
}
/**
* @return Offer|bool
*/
public function getOffer($offerID, $withTeaserMedia = false, $allData = false) {
$offerID = (int)$offerID;
$tarantool = new TarantoolCache(self::SPACE_NAME);
$data = $tarantool->get($offerID);
if (!$data) {
$logger = Logger::instance('DEBUG');
$logger->info($offerID . ' offer not found in tarantool');
return $this->entityManager->getRepository(Offer::class)->findCached($offerID);
}
return $this->getSerializedOffer($data, $withTeaserMedia, $allData);
}
private function getSerializedOffer($data, $withTeaserMedia = false, $allData = false) {
$offerID = $data[0][0];
$normalizer = new ObjectNormalizer();
//$normalizer->setIgnoredAttributes(['bankCurrency']);
$serializer = new Serializer([$normalizer], [new JsonEncoder()]);
/** @var Offer $offer */
$offer = $serializer->deserialize($data[0][1], Offer::class, 'json', [AbstractNormalizer::IGNORED_ATTRIBUTES => ['bankCurrency', 'onlineOrderSettings']]);
$dataDecoded = json_decode($data[0][1]);
$offer->fromJSON($dataDecoded);
if ($withTeaserMedia) {
$medias = $this->getMediaList($offerID, OfferTeaserMedia::TYPE, 0);
if (count($medias) > 0) {
$offer->setTeaserMedias(new ArrayCollection($medias));
}
}
if ($allData) {
$offer->setDetailMeidas($this->getMediaList($offerID, 0, MediaType::TYPE_OFFER_DETAIL_PHOTO_ID));
$offer->setShopMedias($this->getMediaList($offerID, 0, MediaType::TYPE_OFFER_SHOP_PHOTO_ID));
$offer->setDescriptions($this->getEntityDescriptionList($offerID));
$offer->setMapLogoMedias(new ArrayCollection($this->getMediaList($offerID, Media\OfferMapLogoMedia::TYPE, 0)));
$offer->setDeliveryZoneMedias(new ArrayCollection($this->getMediaList($offerID, Media\OfferDeliveryZoneMedia::TYPE, 0)));
$phones2Locations = $this->getPhones2Locations($offerID);
$geoLocations = $this->getGeoLocations(GeoLocation::TYPE, GeoLocation::class, $offerID);
$phoneNumbers = $this->getPhoneNumbers($offerID);
/** @var GeoLocation $geoLocation */
foreach ($geoLocations as $geoLocation) {
foreach ($phoneNumbers as $phoneNumber) {
if (in_array(json_encode([$phoneNumber->getID(), $geoLocation->getID()]), $phones2Locations)) {
$geoLocation->addPhoneNumber($phoneNumber);
}
}
}
$offer->setGeoLocation($geoLocations);
$offer->setPhoneNumbers($phoneNumbers);
$offer->setExtensions($this->getExtensions($offerID));
if ($offer->isActiveCurrencyCalculator()) {
$offer->setBankCurrency(BankCurrency::fromJSON($dataDecoded->bankCurrency));
}
$offer->setSupplierPhotoMedias($this->getMediaList($offerID,MediaType::TYPE_OFFER_SUPPLIER_PHOTO_ID, 0));
$medias = $this->getMediaList($offerID, Media\OfferAppTeaserMedia::TYPE, 0);
if (count($medias) > 0) {
$offer->setAppTeaserMedias(new ArrayCollection($medias));
}
$medias = $this->getMediaList($offerID, Media\OfferTopBlockMedia::TYPE, 0);
if (count($medias) > 0) {
$offer->setTopBlockMedias(new ArrayCollection($medias));
}
$medias = $this->getMediaList($offerID, Media\OfferTopBlockMobileMedia::TYPE, 0);
if (count($medias) > 0) {
$offer->setTopBlockMobileMedias(new ArrayCollection($medias));
}
$medias = $this->getMediaList($offerID, Media\OnlineOrderPopupLogoMedia::TYPE, 0);
if (count($medias) > 0) {
$offer->setOnlineOrderPopupLogoMedias(new ArrayCollection($medias));
}
}
return $offer;
}
public function deleteOffer($offerID) {
$tarantool = new TarantoolCache(self::SPACE_NAME);
$tarantool->callFunction('deleteOffer', [$offerID]);
}
private function getPhoneNumbers($offerID) {
$tarantool = new TarantoolCache(self::PHONE_SPACE_NAME);
$result = $tarantool->select([$offerID],'byEntity');
$phoneNumbers = new ArrayCollection();
foreach ($result as $item) {
$phoneNumbers->add(PhoneNumber::fromJSON(json_decode($item[2])));
}
return $phoneNumbers;
}
private function getPhones2Locations($offerID) {
$tarantool = new TarantoolCache(self::PHONE_2_LOCATION_SPACE_NAME);
$result = $tarantool->select([$offerID],'byOfferID');
$phones2Locations = [];
foreach ($result as $item) {
$phones2Locations[] = json_encode([$item[0], $item[1]]);
}
return $phones2Locations;
}
private function cachePhoneNumbers($offerID, array $phoneNumbers) {
$tarantool = new TarantoolCache(self::PHONE_SPACE_NAME);
$tarantoolPhone2Location = new TarantoolCache(self::PHONE_2_LOCATION_SPACE_NAME);
$phone2Locations = [];
foreach ($phoneNumbers as $number) {
$tarantool->set($number->getID(), [$number->getID(), $offerID, json_encode($number)]);
foreach ($number->getGeoLocations() as $geoLocation) {
$tarantoolPhone2Location->set([$number->getID(), $geoLocation->getID()], [$number->getID(), $geoLocation->getID(), $offerID]);
$phone2Locations[] = json_encode([$number->getID(), $geoLocation->getID()]);
}
}
foreach ($tarantoolPhone2Location->select([$offerID], 'byOfferID') as $phone2Location) {
if (!in_array(json_encode([$phone2Location[0], $phone2Location[1]]), $phone2Locations)) {
$tarantool->callFunction('deletePhoneNumber2geoLocation', [(int)$phone2Location[0], (int)$phone2Location[1]]);
}
}
}
public function deletePhoneNumber($phoneNumberID) {
$tarantool = new TarantoolCache(self::PHONE_SPACE_NAME);
$tarantool->delete($phoneNumberID);
}
public function getExtensions($offerID) {
$tarantool = new TarantoolCache(self::EXTENSION_SPACE_NAME);
$result = $tarantool->select([$offerID], 'byEntityID');
$extensions = new ArrayCollection();
foreach ($result as $item) {
$class = OfferExtension::getExtensionClass($item[1]);
$extensions->add($class::fromJSON(json_decode($item[3])));
}
return $extensions;
}
private function cacheExtensions($offerID, array $extensions) {
$tarantool = new TarantoolCache(self::EXTENSION_SPACE_NAME);
foreach ($extensions as $extension) {
$tarantool->set($extension->getID(), [$extension->getID(), $extension->getType(), $offerID, json_encode($extension)]);
}
}
public function deleteExtension($extensionID) {
$tarantool = new TarantoolCache(self::EXTENSION_SPACE_NAME);
$tarantool->delete($extensionID);
}
public function checkState() {
$tarantool = new TarantoolCache('');
$tarantool->callFunction('deletePastOffers', []);
}
public function getOffers(array $offerIDs, $withTeaserMedia = false, $allData = false) {
$tarantool = new TarantoolCache();
$offers = [];
$offersData = $tarantool->callFunction('getOffers', [$offerIDs]);
if (!isset($offersData[0][0])) {
return $offers;
}
foreach ($offersData[0][0] as $data) {
if (empty($data)) {
continue;
}
$offer = $this->getSerializedOffer($data, $withTeaserMedia, $allData);
$offers[$offer->getID()] = $offer;
}
return $offers;
}
public function findOfferJson(int $offerId): ?Object
{
$tarantool = new TarantoolCache(self::SPACE_NAME);
$offerData = $tarantool->getById($offerId);
if (null === $offerData) {
return null;
}
return json_decode($offerData[1]);
}
public function saveOfferJson(Object $offerJson): void {
$tarantool = new TarantoolCache(self::SPACE_NAME);
$tarantool->set($offerJson->ID, [$offerJson->ID, json_encode($offerJson)]);
}
}