<?php declare(strict_types=1);
namespace Wexo\MarketingTools\Subscriber;
use Shopware\Core\Content\Category\CategoryDefinition;
use Shopware\Core\Content\ProductStream\Service\ProductStreamBuilderInterface;
use Shopware\Core\Framework\DataAbstractionLayer\EntityRepositoryInterface;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Aggregation\Aggregation;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Aggregation\Bucket\FilterAggregation;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Aggregation\Bucket\TermsAggregation;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
use Shopware\Core\Framework\DataAbstractionLayer\Search\EntitySearchResult;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsAnyFilter;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\OrFilter;
use Shopware\Core\System\SalesChannel\SalesChannelContext;
use Shopware\Storefront\Pagelet\Header\HeaderPageletLoadedEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Request;
use Wexo\MarketingTools\Core\Content\MarketingTools\Banner\MarketingBannerCollection;
use Wexo\MarketingTools\Core\Content\MarketingTools\MarketingToolsEntityInterface;
use Wexo\MarketingTools\Core\Content\MarketingTools\Modal\MarketingModalEntity;
use Wexo\MarketingTools\Helper\MarketingToolsHelper;
use Wexo\MarketingTools\Struct\MarketingBannerData;
/**
* Class HeaderPageletLoadedSubscriber
* @package Wexo\MarketingTools\Subscriber
*/
class HeaderPageletLoadedSubscriber implements EventSubscriberInterface
{
const MODAL_COOKIE_IDENTIFIER = 'marketing-modal-';
/** @var EntityRepositoryInterface */
protected $marketingBannerRepository;
/** @var EntityRepositoryInterface */
protected $marketingModalRepository;
/** @var EntityRepositoryInterface */
protected $productRepository;
/** @var MarketingToolsHelper */
protected $helper;
/** @var ProductStreamBuilderInterface */
protected $productStreamBuilder;
/**
* HeaderPageletLoadedSubscriber constructor.
* @param EntityRepositoryInterface $marketingBannerRepository
* @param EntityRepositoryInterface $marketingModalRepository
* @param EntityRepositoryInterface $productRepository
* @param MarketingToolsHelper $marketingToolsHelper
* @param ProductStreamBuilderInterface $productStreamBuilder
*/
public function __construct(
EntityRepositoryInterface $marketingBannerRepository,
EntityRepositoryInterface $marketingModalRepository,
EntityRepositoryInterface $productRepository,
MarketingToolsHelper $marketingToolsHelper,
ProductStreamBuilderInterface $productStreamBuilder
) {
$this->marketingBannerRepository = $marketingBannerRepository;
$this->marketingModalRepository = $marketingModalRepository;
$this->productRepository = $productRepository;
$this->helper = $marketingToolsHelper;
$this->productStreamBuilder = $productStreamBuilder;
}
/**
* @return string[]
*/
public static function getSubscribedEvents()
{
return [
HeaderPageletLoadedEvent::class => 'onHeaderPagelet'
];
}
/**
* @param HeaderPageletLoadedEvent $event
*/
public function onHeaderPagelet(HeaderPageletLoadedEvent $event)
{
$request = $event->getRequest();
if (!$request->get('productId', null)
&& (!$request->get('navigationId') && $request->get('_route') !== 'frontend.home.page')
) {
return;
}
$criteria = $this->helper->getValidationCriteria($event->getSalesChannelContext());
$criteria->addAssociation('products.product');
$criteria->addAssociation('categories');
$banners = $this->getBanners($event, $criteria);
$modals = $this->getModals($event, $criteria);
$event->getPagelet()->addExtension(
'marketing_banners',
$this->getBannerData(
$this->modifyResult($banners, $event)
)
);
$event->getPagelet()->addExtension(
'marketing_modal',
$this->getModalData(
$this->modifyResult($modals, $event),
$request
)
);
}
/**
* @param EntitySearchResult $result
* @param Request $request
* @return MarketingModalEntity|null
*/
protected function getModalData(EntitySearchResult $result, Request $request): ?MarketingModalEntity
{
// Find all cookies set by modals
$tokens = array_filter($request->cookies->all(), function ($key) {
return strpos($key, self::MODAL_COOKIE_IDENTIFIER) !== false;
}, ARRAY_FILTER_USE_KEY);
// Filter the result to only return modals whose token is not set in the cookies
$result = $result->filter(function ($item) use ($tokens) {
return !in_array($item->getToken(), $tokens);
});
// Return the first found or null if none are found
return $result->first();
}
/**
* @param EntitySearchResult $result
* @return MarketingBannerData
*/
protected function getBannerData(EntitySearchResult $result): MarketingBannerData
{
$data = new MarketingBannerData();
if ($fullAggregation = $result->getAggregations()->get('full')) {
/** @var MarketingBannerCollection $fullCollection */
$fullCollection = $result->getEntities()->getList($fullAggregation->getKeys());
$data->setFull($fullCollection);
}
if ($halfAggregation = $result->getAggregations()->get('half')) {
/** @var MarketingBannerCollection $halfCollection */
$halfCollection = $result->getEntities()->getList($halfAggregation->getKeys());
// This allows half banners to be combined
/*if ($halfCollection->count() && $halfCollection->count() % 2 !== 0) {
$last = $halfCollection->getKeys()[$halfCollection->count()-1];
$data->getFull()->add($halfCollection->get($last));
$halfCollection->remove($last);
}*/
$data->setHalf($halfCollection);
}
return $data;
}
/**
* @param HeaderPageletLoadedEvent $event
* @param Criteria $criteria
* @return EntitySearchResult
*/
protected function getBanners(HeaderPageletLoadedEvent $event, Criteria $criteria): EntitySearchResult
{
$bannerCriteria = clone($criteria);
$bannerCriteria->addAggregation($this->getBannerWidthAggregation('half'));
$bannerCriteria->addAggregation($this->getBannerWidthAggregation('full'));
return $this->marketingBannerRepository->search($bannerCriteria, $event->getContext());
}
/**
* @param HeaderPageletLoadedEvent $event
* @param Criteria $criteria
* @return EntitySearchResult
*/
protected function getModals(HeaderPageletLoadedEvent $event, Criteria $criteria): EntitySearchResult
{
return $this->marketingModalRepository->search($criteria, $event->getContext());
}
/**
* @param string $bannerWidth
* @return Aggregation
*/
private function getBannerWidthAggregation(string $bannerWidth): Aggregation
{
return new FilterAggregation(
"$bannerWidth-filter",
new TermsAggregation($bannerWidth, 'id'),
[
new EqualsFilter('bannerWidth', $bannerWidth)
]
);
}
/**
* @param EntitySearchResult $result
* @param HeaderPageletLoadedEvent $event
* @return EntitySearchResult
*/
private function modifyResult(EntitySearchResult $result, HeaderPageletLoadedEvent $event): EntitySearchResult
{
$navigation = $event->getPagelet()->getNavigation();
$activeCategoryId = $navigation->getActive() ? $navigation->getActive()->getId() : null;
$productId = $event->getRequest()->get('productId', null);
/** @var MarketingToolsEntityInterface $entity */
foreach ($result->getEntities() as $entity) {
$condition = false;
// If we are on a product page only check product condition
// This is because the product page does not count as a category page
// And if the current page is not a navigation page shopware will default navigationId to the home category
if ($productId) {
if ($assignedProductIds = $this->getAssignedProducts($entity, $event->getSalesChannelContext())) {
$condition = in_array($productId, $assignedProductIds);
}
} elseif ($activeCategoryId && $assignedCategories = $entity->getCategories()) {
$condition = in_array($activeCategoryId, $assignedCategories->getCategoryIds());
}
if (!$condition) {
$result->remove($entity->getId());
$result->getEntities()->remove($entity->getId());
}
}
return $result;
}
/**
* @param MarketingToolsEntityInterface $entity
* @param SalesChannelContext $context
* @return array
*/
private function getAssignedProducts(MarketingToolsEntityInterface $entity, SalesChannelContext $context): array
{
if ($entity->getProductAssignmentType() === CategoryDefinition::PRODUCT_ASSIGNMENT_TYPE_PRODUCT_STREAM &&
$entity->getProductStreamId() !== null
) {
$filters = $this->productStreamBuilder->buildFilters(
$entity->getProductStreamId(),
$context->getContext()
);
$criteria = new Criteria();
$criteria->addFilter(...$filters);
$products = $this->productRepository->searchIds($criteria, $context->getContext())->getIds();
} else {
$products = $entity->getProducts()->getProductIds();
}
return $products;
}
}