<?php declare(strict_types=1);
namespace Acris\ShopSwitch\Subscriber;
use Acris\ShopSwitch\Components\ShopSwitchHelperService;
use Acris\ShopSwitch\Components\ShopSwitchRedirectUrlService;
use Acris\ShopSwitch\Components\ShopSwitchRuleGateway;
use Acris\ShopSwitch\Components\ShopSwitchRuleService;
use Acris\ShopSwitch\Components\Struct\ShopSwitchHeaderDataStruct;
use Acris\ShopSwitch\Custom\ShopSwitchRuleDefinition;
use Acris\ShopSwitch\Custom\ShopSwitchRuleEntity;
use Shopware\Core\Framework\Adapter\Cache\AbstractCacheTracer;
use Shopware\Core\Framework\Adapter\Cache\CacheValueCompressor;
use Shopware\Core\SalesChannelRequest;
use Shopware\Core\System\SalesChannel\SalesChannelContext;
use Shopware\Storefront\Pagelet\Header\HeaderPageletLoadedEvent;
use Symfony\Component\Cache\Adapter\TagAwareAdapterInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Contracts\Cache\ItemInterface;
use Shopware\Core\Framework\Context;
use Symfony\Component\HttpFoundation\Request;
class ShopSwitchHeaderSubscriber implements EventSubscriberInterface
{
private ShopSwitchRuleGateway $shopSwitchRuleGateway;
private ShopSwitchHelperService $helperService;
private ShopSwitchRedirectUrlService $redirectUrlService;
private ShopSwitchRuleService $shopSwitchRuleService;
private TagAwareAdapterInterface $cache;
private AbstractCacheTracer $tracer;
public function __construct(
ShopSwitchRuleGateway $shopSwitchRuleGateway,
ShopSwitchHelperService $helperService,
ShopSwitchRedirectUrlService $redirectUrlService,
ShopSwitchRuleService $shopSwitchRuleService,
TagAwareAdapterInterface $cache,
AbstractCacheTracer $tracer
)
{
$this->shopSwitchRuleGateway = $shopSwitchRuleGateway;
$this->helperService = $helperService;
$this->redirectUrlService = $redirectUrlService;
$this->shopSwitchRuleService = $shopSwitchRuleService;
$this->cache = $cache;
$this->tracer = $tracer;
}
public static function getSubscribedEvents()
{
return [
HeaderPageletLoadedEvent::class => 'onHeaderPageletLoaded'
];
}
public function onHeaderPageletLoaded(HeaderPageletLoadedEvent $event): void
{
$request = $event->getRequest();
$salesChannelContext = $event->getSalesChannelContext();
$activeDomainId = $request->get(SalesChannelRequest::ATTRIBUTE_DOMAIN_ID);
if(empty($activeDomainId)) return;
$key = $this->generateKey($activeDomainId, $request->getRequestUri());
// compatibility with Shopware < 6.4.11.0
// TODO: Remove when Shopware minimum version changes to higher 6.4.11.0
if(method_exists($this->cache, 'get')) {
$struct = $this->getFromCacheCurrent($key, $activeDomainId, $request, $salesChannelContext);
} else {
$struct = $this->getFromCacheSmallerSw64110($key, $activeDomainId, $request, $salesChannelContext);
}
if ($struct instanceof ShopSwitchHeaderDataStruct) {
$salesChannelContext->getContext()->addExtension(ShopSwitchHeaderDataStruct::KEY, $struct);
}
}
private function getFromCacheCurrent($key, string $activeDomainId, Request $request, SalesChannelContext $salesChannelContext): ?ShopSwitchHeaderDataStruct
{
$value = $this->cache->get($key, function (ItemInterface $item) use ($activeDomainId, $request, $salesChannelContext) {
$name = self::buildName($activeDomainId);
$activeRule = $this->shopSwitchRuleGateway->getActiveRuleByDomain($activeDomainId, $salesChannelContext->getContext());
if (!$activeRule instanceof ShopSwitchRuleEntity || $activeRule->getHeaderCountrySelect() !== ShopSwitchRuleDefinition::RULE_DEFINITION_HEADER_COUNTRY_SELECT_SHOW
|| $activeRule->getRuleDefinitionType() === ShopSwitchRuleDefinition::RULE_DEFINITION_TYPE_LANGUAGE) {
return CacheValueCompressor::compress(null);
}
$response = $this->tracer->trace($name, function () use ($activeRule, $activeDomainId, $request, $salesChannelContext) {
return $this->getDataStruct($activeRule, $activeDomainId, $request, $salesChannelContext);
});
$item->tag($this->generateTags($activeDomainId));
return CacheValueCompressor::compress($response);
});
return CacheValueCompressor::uncompress($value);
}
private function getFromCacheSmallerSw64110($key, string $activeDomainId, Request $request, SalesChannelContext $salesChannelContext): ?ShopSwitchHeaderDataStruct
{
/** @var ItemInterface $item */
$item = $this->cache->getItem($key);
if($item->isHit() === true) {
return $item->get();
}
$name = self::buildName($activeDomainId);
$activeRule = $this->shopSwitchRuleGateway->getActiveRuleByDomain($activeDomainId, $salesChannelContext->getContext());
if (!$activeRule instanceof ShopSwitchRuleEntity || $activeRule->getHeaderCountrySelect() !== ShopSwitchRuleDefinition::RULE_DEFINITION_HEADER_COUNTRY_SELECT_SHOW
|| $activeRule->getRuleDefinitionType() === ShopSwitchRuleDefinition::RULE_DEFINITION_TYPE_LANGUAGE) {
$struct = null;
} else {
$struct = $this->tracer->trace($name, function () use ($activeRule, $activeDomainId, $request, $salesChannelContext) {
return $this->getDataStruct($activeRule, $activeDomainId, $request, $salesChannelContext);
});
$item->tag($this->generateTags($activeDomainId));
}
$item->set($struct);
$this->cache->saveDeferred($item);
return $struct;
}
private function getDataStruct(ShopSwitchRuleEntity $activeRule, string $activeDomainId, Request $request, SalesChannelContext $salesChannelContext): ShopSwitchHeaderDataStruct
{
$context = $salesChannelContext->getContext();
$activeDomainEntity = $this->helperService->getDomainByIdAndSalesChannelContext($activeDomainId, $salesChannelContext);
$activeDomainUrl = $this->redirectUrlService->getUrlForActiveDomain($request, $activeDomainEntity);
$possibleLanguageCollection = $this->helperService->getPossibleLanguages($activeRule, $request, $activeDomainUrl, $context);
$rawPossibleCountryCollection = $this->helperService->getRawPossibleCountries($activeRule, $request, $activeDomainUrl, $context);
if ($rawPossibleCountryCollection->count() > 0 && $possibleLanguageCollection->count() > 0) {
$this->helperService->removeNotExistingCombinations($rawPossibleCountryCollection, $possibleLanguageCollection);
}
$activeRuleDomain = $this->shopSwitchRuleService->getActiveRuleDomain($activeDomainId, $activeRule);
// $rawPossibleCountries do not consist of active countries, so default country.
$countryId = $activeRuleDomain->getDefaultShippingCountryId();
return new ShopSwitchHeaderDataStruct($rawPossibleCountryCollection, $activeDomainUrl, $activeDomainUrl, $activeRule, $countryId);
}
public static function buildName(string $activeDomainId): string
{
return 'shop-switch-' . $activeDomainId;
}
private function generateKey(string $activeDomainId, string $requestUri): string
{
return 'shop-switch-' . md5(json_encode($activeDomainId . '-' . $requestUri));
}
private function generateTags(string $activeDomainId): array
{
$tags = array_merge(
$this->tracer->get(self::buildName($activeDomainId)),
[self::buildName($activeDomainId)],
[ShopSwitchCacheSubscriber::HEADER_CACHE_TAG],
['sales-channel'],
['sales-channel-context'],
['navigation']
);
return array_unique($tags);
}
}