<?php declare(strict_types=1);
/**
* Shopware
* Copyright © 2021
*
* @category Shopware
* @package SwpProductOptionsSix
* @subpackage ListingSubscriber.php
*
* @copyright 2021 Iguana-Labs GmbH
* @author Module Factory <info at module-factory.com>
* @license https://www.module-factory.com/eula
*/
namespace Swp\ProductOptionsSix\Subscriber\Listing;
use Shopware\Core\Content\Cms\Aggregate\CmsSlot\CmsSlotEntity;
use Shopware\Core\Content\Cms\CmsPageCollection;
use Shopware\Core\Content\Cms\CmsPageEntity;
use Shopware\Core\Content\Cms\Events\CmsPageLoadedEvent;
use Shopware\Core\Content\Cms\SalesChannel\Struct\ProductBoxStruct;
use Shopware\Core\Content\Product\Events\ProductListingResultEvent;
use Shopware\Core\Content\Product\SalesChannel\SalesChannelProductEntity;
use Shopware\Core\Framework\Context;
use Shopware\Core\Framework\DataAbstractionLayer\EntityRepository;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
use Shopware\Core\Framework\Struct\ArrayStruct;
use Shopware\Core\System\SalesChannel\SalesChannelContext;
use Shopware\Core\System\SystemConfig\SystemConfigService;
use Shopware\Storefront\Page\Search\SearchPageLoadedEvent;
use Swp\ProductOptionsSix\Core\Content\ProductOptions\SalesChannel\Filter\ProductOptionsFilter;
use Swp\ProductOptionsSix\Core\Content\ProductOptions\SalesChannel\ProductOptionsLoader;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class ListingSubscriber implements EventSubscriberInterface
{
/** @var SystemConfigService */
private $systemConfigService;
/** @var EntityRepository */
private $productRepository;
/** @var EntityRepository */
private $productOptionRepository;
/** @var ProductOptionsFilter */
private $productOptionsFilter;
/**
* ListingSubscriber constructor.
*
* @param SystemConfigService $systemConfigService
* @param EntityRepository $productRepository
* @param EntityRepository $productOptionRepository
* @param ProductOptionsFilter $productOptionsFilter
*/
public function __construct(
SystemConfigService $systemConfigService,
EntityRepository $productRepository,
EntityRepository $productOptionRepository,
ProductOptionsFilter $productOptionsFilter
) {
$this->systemConfigService = $systemConfigService;
$this->productRepository = $productRepository;
$this->productOptionRepository = $productOptionRepository;
$this->productOptionsFilter = $productOptionsFilter;
}
/**
* @return array
*/
public static function getSubscribedEvents(): array
{
return [
CmsPageLoadedEvent::class => 'onCmsPageLoaded',
SearchPageLoadedEvent::class => 'onSearchPageLoaded',
ProductListingResultEvent::class => 'onProductListingResult'
];
}
/**
* @param CmsPageLoadedEvent $event
*/
public function onCmsPageLoaded(CmsPageLoadedEvent $event) :void
{
if (!$this->isCannelActive($event->getSalesChannelContext())) {
return;
}
/** @var CmsPageCollection $cms */
$cms = $event->getResult();
/** @var CmsPageEntity $cmsEntity */
foreach ($cms->getIterator() as $cmsEntity) {
//Product
$productBoxes = $cmsEntity->getElementsOfType('product-box');
/** @var CmsSlotEntity $productBox */
foreach ($productBoxes as $productBox) {
/** @var ProductBoxStruct $box */
$box=$productBox->getData();
/** @var SalesChannelProductEntity $product */
$product = $box->getProduct();
$this->addExtensionToProduct($product, $event->getContext());
}
//Slider
$productBoxes = $cmsEntity->getElementsOfType('product-slider');
/** @var CmsSlotEntity $productBox */
foreach ($productBoxes as $productBox) {
$sliderProducts = $productBox->getData()->getProducts();
if ($sliderProducts === null) {
continue;
}
foreach ($sliderProducts->getElements() as $product) {
$this->addExtensionToProduct($product, $event->getContext());
}
}
//CrossSelling
$productBoxes = $cmsEntity->getElementsOfType('cross-selling');
/** @var CmsSlotEntity $productBox */
foreach ($productBoxes as $productBox) {
$crossSellings = $productBox->getData()->getCrossSellings();
if ($crossSellings === null) {
continue;
}
foreach ($crossSellings->getElements() as $crossSelling) {
$products = $crossSelling->getProducts()->getElements();
foreach ($products as $product) {
$this->addExtensionToProduct($product, $event->getContext());
}
}
}
}
}
/**
* @param SearchPageLoadedEvent $event
*/
public function onSearchPageLoaded(SearchPageLoadedEvent $event): void
{
if (!$this->isCannelActive($event->getSalesChannelContext())) {
return;
}
$elements = $event->getPage()->getListing()->getElements();
/** @var SalesChannelProductEntity $product */
foreach($elements as $product) {
$this->addExtensionToProduct($product, $event->getContext());
}
}
/**
* @param ProductListingResultEvent $event
*/
public function onProductListingResult(ProductListingResultEvent $event): void
{
if (!$this->isCannelActive($event->getSalesChannelContext())) {
return;
}
$elements = $event->getResult()->getElements();
/** @var SalesChannelProductEntity $product */
foreach($elements as $product) {
$this->addExtensionToProduct($product, $event->getContext());
}
}
/**
* @param SalesChannelProductEntity $product
* @param Context $context
* @return void
*/
private function addExtensionToProduct(
SalesChannelProductEntity $product,
Context $context
):void {
$hasOptions = $this->productHasOptions($product, $context);
// add to extensions
$product->addExtension(
ProductOptionsLoader::PRODUCT_OPTIONS_EXT_KEY,
(new ArrayStruct())->assign(['hasProductOptions' => $hasOptions])
);
}
/**
* @param SalesChannelProductEntity $product
* @param Context $context
* @return bool
*/
private function productHasOptions(SalesChannelProductEntity $product, Context $context): bool
{
$criteria = new Criteria();
$criteria->addAssociations([
'group.productoptionsgroup',
'option.productoptionsoption'
]);
$productId = $this->productOptionsFilter->getProductid($product->getId(), $context);
$criteria->addFilter(
new EqualsFilter('productId', $productId)
);
$criteria->addFilter(
$this->productOptionsFilter->getFilter()
);
$total = $this->productOptionRepository->searchIds(
$criteria, $context
)->getTotal();
return $total > 0;
}
/**
* @param SalesChannelContext $context
* @return array|mixed|null
*/
private function isCannelActive(SalesChannelContext $context)
{
return $this->systemConfigService->get(
'SwpProductOptionsSix.config.channelActive',
$context->getSalesChannel()->getId()
);
}
}