<?php declare(strict_types=1);
namespace Semes\RefererBreadcrumb\Subscriber;
use Shopware\Core\Framework\DataAbstractionLayer\Exception\InconsistentCriteriaIdsException;
use Shopware\Storefront\Page\Product\ProductPageLoadedEvent;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\OrFilter;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\ContainsFilter;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsAnyFilter;
use Shopware\Core\Content\Category\CategoryEntity;
use Shopware\Core\Content\Product\Events\ProductDetailRouteCacheTagsEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Shopware\Core\Framework\Adapter\Cache\CacheInvalidator;
use Shopware\Core\System\SystemConfig\SystemConfigService;
use Shopware\Core\System\SalesChannel\SalesChannelContext;
class FrontendSubscriber implements EventSubscriberInterface
{
public $categoryRepository;
public $systemConfigService;
public $cacheInvalidator;
public $refererCategory;
/**
* @return array
*/
public static function getSubscribedEvents(): array
{
return [
ProductPageLoadedEvent::class => 'onProductPageLoaded',
ProductDetailRouteCacheTagsEvent::class => 'onProductDetailRouteCacheTagsEvent',
//KernelEvents::CONTROLLER => 'invalidateCache',
KernelEvents::REQUEST => 'invalidateCache'
];
}
/**
*
* @param ProductDetailRouteCacheTagsEvent $event
* @return void
* @throws InconsistentCriteriaIdsException
*/
public function onProductDetailRouteCacheTagsEvent($event)
{
$event->addTags(['referer-category']);
}
/**
*
* @param ProductPageLoadedEvent $event
* @return void
* @throws InconsistentCriteriaIdsException
*/
public function onProductPageLoaded($event)
{
$page = $event->getPage();
$salesChannelContext = $event->getSalesChannelContext();
$salesChannel = $salesChannelContext->getSalesChannel();
// get product categories
$product = $page->getProduct();
if(!$product) return;
// try to get path from referer
if($refererCategory = $this->getRefererCategory($salesChannelContext, $salesChannel, $product)){
$page->getHeader()->getNavigation()->setActive($refererCategory); // set active navigation item
$product->setSeoCategory($refererCategory); // sets breadcrumb
return;
}
}
private function getRefererCategory($salesChannelContext, $salesChannel, $product)
{
if(!is_null($this->refererCategory)) return $this->refererCategory;
$url = $_SERVER['HTTP_REFERER'] ?? '';
foreach ($salesChannel->getDomains() as $domain) {
$url = stripos($url, $domain->getUrl()) === 0 ? (str_replace($domain->getUrl(), '', $url)) : $url;
}
$url = explode('?', $url, 2);
$url = $url[0];
$url = trim(strtolower($url), '/');
$this->refererCategory = $this->getCategoryByUrl($url, $salesChannelContext, $salesChannel, $product);
return $this->refererCategory;
}
/**
*
* @param string $url
* @param mixed $context
* @return CategoryEntity|false
* @throws InconsistentCriteriaIdsException
*/
private function getCategoryByUrl($url, $salesChannelContext, $salesChannel, $product)
{
$allowedBreadcrumbCategories = $this->systemConfigService->get("SemesRefererBreadcrumb.config.allowedBreadcrumbCategories") ?? 'all';
$criteria = new Criteria();
$criteria->addAssociation('seoUrls');
$criteria->addFilter(new EqualsFilter('active', true));
$categoryUrls = [];
if($allowedBreadcrumbCategories == 'onlyProductCategories'){
$categoryUrls = $product->getCategoryIds();
} elseif($allowedBreadcrumbCategories == 'onlyProductCategoriesAndParents'){
$categoryUrls = $product->getCategoryTree();
}
if($categoryUrls){
$criteria->addFilter(new EqualsAnyFilter('id', $categoryUrls));
}
$criteria->addFilter($this->getSalesChannelFilter($salesChannelContext));
$categories = $this->categoryRepository->search($criteria, $salesChannelContext->getContext());
foreach ($categories as $category) {
/* @var $category CategoryEntity */
foreach ($category->getSeoUrls() as $seoUrl) {
if($seoUrl->getSalesChannelId() != $salesChannel->getId()) continue;
if(trim(strtolower($seoUrl->getSeoPathInfo()), '/') == $url){
return $category;
}
if(trim(strtolower($seoUrl->getPathInfo()), '/') == $url){
return $category;
}
}
}
return false;
}
private function getSalesChannelFilter(SalesChannelContext $context)
{
$ids = array_filter([
$context->getSalesChannel()->getNavigationCategoryId(),
$context->getSalesChannel()->getServiceCategoryId(),
$context->getSalesChannel()->getFooterCategoryId(),
]);
return new OrFilter(array_map(static function (string $id) {
return new ContainsFilter('path', '|' . $id . '|');
}, $ids));
}
public function invalidateCache()
{
$this->cacheInvalidator->invalidate(['referer-category']);
}
}